├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cdk ├── .gitignore ├── .npmignore ├── .prettierrc ├── bin │ └── cdk.ts ├── cdk.context.json ├── cdk.json ├── issuerProfile │ └── .placeholder ├── lambda │ ├── nodejs │ │ ├── AddAttribute.js │ │ ├── AddDelegate.js │ │ ├── ChangeDIDOwner.js │ │ ├── CreateAccount.js │ │ ├── DownloadVC.js │ │ ├── GenerateIdentifier.js │ │ ├── GetDIDDocument.js │ │ ├── GetDIDOwner.js │ │ ├── GetIdentifier.js │ │ ├── IssueJWT.js │ │ ├── RevokeAttribute.js │ │ ├── RevokeDelegate.js │ │ ├── VerifyJWT.js │ │ ├── VerifyVC.js │ │ ├── lib │ │ │ └── dynamodbHelper.js │ │ └── package.json │ └── python │ │ └── Issue_vc │ │ ├── conf-template.ini │ │ ├── issue_vc.py │ │ ├── requirements.txt │ │ └── template │ │ └── verifiable-credential.json.j2 ├── lib │ ├── RemoteOutputStack.ts │ ├── holderWebapp.ts │ ├── issuerProfileBucketStack.ts │ ├── issuerProfileDestributionStack.ts │ ├── issuerWebapp.ts │ ├── resources │ │ ├── HolderBackend.ts │ │ ├── IssueBackend.ts │ │ ├── VerifierBackend.ts │ │ └── dynamodb.ts │ ├── verifierWebapp.ts │ └── wafStack.ts ├── package-lock.json ├── package.json ├── tsconfig.json └── waf │ ├── apiWafRule.ts │ ├── cloudfrontWafRule.ts │ └── webappWafRule.ts ├── docs ├── DeployEnvironment │ ├── 1_CreateIssuerProfile.md │ ├── 2_DeployIssuerWebApp.md │ ├── 3_DeployHolderWebApp.md │ └── 4_DeployVerifierWebApp.md ├── HowToUse │ ├── IssuingVCWithCLI.md │ └── IssuingVCWithGUI.md ├── VerifyVC.md ├── images │ ├── Architecture.png │ ├── DIDの概要図.png │ ├── GetVCArchitecture.png │ ├── holder_webapp.png │ ├── image.drawio │ ├── issue_vc.png │ ├── issuer_profile.png │ ├── issuer_webapp.png │ ├── sigin.png │ ├── signup.png │ └── verifier_webapp.png ├── issuerProfileTemplate │ ├── issuer-profile-template.json │ └── revocation-list-template.json └── spec │ ├── Holder_API.yaml │ ├── HowVerifierGetVC.md │ ├── Issuer_API.yaml │ └── Verifier_API.yaml ├── holderwebapp ├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── dist │ └── .gitkeep ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── components │ │ ├── AttributeDID.vue │ │ ├── ChangeDIDOwner.vue │ │ ├── CheckDIDOwner.vue │ │ ├── DelegateDID.vue │ │ ├── GenerateIdentifier.vue │ │ ├── GetDIDDocument.vue │ │ ├── GetIdentifier.vue │ │ ├── IssueJWT.vue │ │ ├── ListVC.vue │ │ ├── SelectKeyPair.vue │ │ ├── UploadVC.vue │ │ └── VerifyJWT.vue │ ├── lib │ │ └── utils.ts │ ├── main.ts │ ├── plugins │ │ ├── vuetify.ts │ │ └── webfontloader.ts │ ├── router │ │ └── index.ts │ ├── shims-vue.d.ts │ └── views │ │ ├── HolderView.vue │ │ ├── HomeView.vue │ │ └── SignIn.vue ├── tsconfig.json └── vue.config.js ├── issuerwebapp ├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── dist │ └── .gitkeep ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── components │ │ └── IssureVC.vue │ ├── lib │ │ └── utils.ts │ ├── main.ts │ ├── plugins │ │ ├── vuetify.ts │ │ └── webfontloader.ts │ ├── router │ │ └── index.ts │ ├── shims-vue.d.ts │ └── views │ │ ├── IssueView.vue │ │ └── SignIn.vue ├── tsconfig.json └── vue.config.js ├── utils ├── cert-verifier.js ├── createWallet.js ├── decodeCertProofValue.js └── package.json └── verifierwebapp ├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── babel.config.js ├── dist └── .gitkeep ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── components │ ├── CheckDIDOwner.vue │ ├── DownloadVc.vue │ ├── GenerateIdentifier.vue │ ├── GetDIDDocument.vue │ ├── GetIdentifier.vue │ ├── IssueJWT.vue │ ├── ListVC.vue │ ├── Metamask.vue │ ├── SelectKeyPair.vue │ ├── UploadVC.vue │ ├── VerifyJWT.vue │ └── VerifyVC.vue ├── lib │ └── utils.ts ├── main.ts ├── plugins │ ├── vuetify.ts │ └── webfontloader.ts ├── router │ └── index.ts ├── shims-vue.d.ts └── views │ ├── HomeView.vue │ └── SignIn.vue ├── tsconfig.json └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | */node_modules/* 2 | .env 3 | cdk/lambda/conf.ini 4 | cdk/lambda/python/Issue_vc/conf.ini 5 | cdk/issuerProfile/*.json 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## プロジェクト概要/Project Detailes 2 | 3 | W3Cの標準化規格であるDIDs v1.0で実装したdecentralized identityのサンプルコードです。 4 | DID Documentでアクセス権限の設定をした相手にだけにVerifiable Credentialのアクセスを許可する仕組みをAWSで実装しました。 5 | このサンプルコードはEthereumのテストネットで稼働します。 6 | This is sample code implemented in DIDs(decentralized identity) v1.0, which is a standardized standard for W3C. 7 | We have implemented a mechanism in AWS that allows access to Verifiable Credentials only to those for whom access authority has been set in DID Document. 8 | This sample code will be running on Ethereum testnet. 9 | 10 | このプロジェクトは以下のことができます。 11 | This project able to these functions. 12 | 13 | - IssuerがVerifiableCredentialの発行 14 | Issuer create a VerifiableCredential 15 | - Holderが自分のVerifiableCredentialを保管。 16 | Holder can store own VerifiableCredential 17 | - HolderがVerifiableCredentialからDID Documentを作成 18 | Holder creates DID Document from verifiableCredential 19 | - VerifierがJWTを使用してHolderのVerifiableCredentialにアクセス 20 | Verifier uses JWT to access Holder's VerifiableCredential 21 | - VerifierがVerifiableCredentialの正しさを検証 22 | Verifier verifies the correctness of VerifiableCredential 23 | 24 | 25 | ## DIDの概要図/Overview  26 | ![](./docs/images/DID%E3%81%AE%E6%A6%82%E8%A6%81%E5%9B%B3.png) 27 | 28 | ## アーキテクチャ/Architecture 29 | 30 | ![](./docs/images/Architecture.png) 31 | 32 | 33 | ## イメージ/Screenshot 34 | 35 | - Issuerの操作画面/Issuer operation window 36 | ![](./docs/images/issuer_webapp.png) 37 | - Holderの操作画面/Holder operation window 38 | ![](./docs/images/holder_webapp.png) 39 | - Verifierの操作画面/Verifier operation window 40 | ![](./docs/images/verifier_webapp.png) 41 | 42 | 43 | # ドキュメント/Documents 44 | - [環境構築手順/How to deploy](./docs/DeployEnvironment) 45 | - [API仕様書/API document](./docs/spec/) 46 | - [使い方/How to Use](./docs/HowToUse/) 47 | 48 | ## Depends On 49 | - Blockcerts: https://github.com/blockchain-certificates/cert-verifier-js 50 | - ethr-did: https://github.com/uport-project/ethr-did 51 | - ethr-did-resolver: https://github.com/decentralized-identity/ethr-did-resolver 52 | 53 | ## Security 54 | 55 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 56 | 57 | ## License 58 | 59 | This library is licensed under the MIT-0 License. See the LICENSE file. 60 | -------------------------------------------------------------------------------- /cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | -------------------------------------------------------------------------------- /cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /cdk/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false 4 | } 5 | -------------------------------------------------------------------------------- /cdk/bin/cdk.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "source-map-support/register"; 3 | import { App, CfnOutput, Aspects } from "aws-cdk-lib"; 4 | import { IssuerProfileBucketStack } from "../lib/issuerProfileBucketStack"; 5 | import { IssuerProfileDestributionStack } from "../lib/issuerProfileDestributionStack"; 6 | import { WafStack } from "../lib/wafStack"; 7 | import { RemoteOutputStack } from "../lib/RemoteOutputStack"; 8 | import { IssuerWebapp } from "../lib/issuerWebapp"; 9 | import { HolderWebapp } from "../lib/holderWebapp"; 10 | import { VerifierWebapp } from "../lib/verifierWebapp"; 11 | import { AwsPrototypingChecks } from "@aws-prototyping-sdk/pdk-nag"; 12 | 13 | const app = new App(); 14 | Aspects.of(app).add(new AwsPrototypingChecks()); 15 | 16 | // CloudFront用のWAFはus-east-1でしか作れない。 17 | const webAcl = new WafStack(app, "DIDWafStack", { 18 | env: { 19 | region: "us-east-1", 20 | }, 21 | }); 22 | new CfnOutput(webAcl, "issuerProfileWafArn", { 23 | value: webAcl.issuerProfileWaf, 24 | }); 25 | new CfnOutput(webAcl, "webappWafArn", { value: webAcl.webappWaf }); 26 | 27 | // クロスリージョン参照用 28 | const remoteOutput = new RemoteOutputStack(app, "RemoteOutput", { 29 | webAcl, 30 | }); 31 | 32 | const issuerProfileBucketStack = new IssuerProfileBucketStack( 33 | app, 34 | "DIDIssuerProfileBucketStack", 35 | { 36 | webAclArn: remoteOutput.issuerProfileWafArn, 37 | } 38 | ); 39 | issuerProfileBucketStack.addDependency(webAcl); 40 | issuerProfileBucketStack.addDependency(remoteOutput); 41 | 42 | const issuerProfileDestributionStack = new IssuerProfileDestributionStack( 43 | app, 44 | "DIDIssuerProfileDestributionStack", 45 | { 46 | issuerProfileBucket: issuerProfileBucketStack.issuerProfileBucket, 47 | distribution: issuerProfileBucketStack.distribution, 48 | } 49 | ); 50 | issuerProfileDestributionStack.addDependency(issuerProfileBucketStack); 51 | 52 | const issuerWebapp = new IssuerWebapp(app, "IssuerWebapp", { 53 | webAclArn: remoteOutput.webappWafArn, 54 | }); 55 | issuerWebapp.addDependency(webAcl); 56 | issuerWebapp.addDependency(remoteOutput); 57 | 58 | const holderWebapp = new HolderWebapp(app, "HolderWebapp", { 59 | webAclArn: remoteOutput.webappWafArn, 60 | }); 61 | holderWebapp.addDependency(webAcl); 62 | holderWebapp.addDependency(remoteOutput); 63 | 64 | const verifierWebapp = new VerifierWebapp(app, "VerifierWebapp", { 65 | webAclArn: remoteOutput.webappWafArn, 66 | }); 67 | verifierWebapp.addDependency(webAcl); 68 | verifierWebapp.addDependency(remoteOutput); 69 | -------------------------------------------------------------------------------- /cdk/cdk.context.json: -------------------------------------------------------------------------------- 1 | { 2 | "acknowledged-issue-numbers": [ 3 | 21902 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/cdk.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 21 | "@aws-cdk/core:stackRelativeExports": true, 22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 24 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 25 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/core:checkSecretUsage": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 31 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 32 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 33 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 34 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 35 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 36 | "@aws-cdk/core:enablePartitionLiterals": true, 37 | "@aws-cdk/core:target-partitions": [ 38 | "aws", 39 | "aws-cn" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cdk/issuerProfile/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/cdk/issuerProfile/.placeholder -------------------------------------------------------------------------------- /cdk/lambda/nodejs/AddAttribute.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | 5 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 6 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 7 | 8 | exports.handler = async function (event) { 9 | try { 10 | const body = JSON.parse(event.body); 11 | console.log(body); 12 | const identifier = body.identifier; 13 | const address = body.signerAddress; 14 | const attribute_path = body.attribute_path; 15 | const attribute_param = body.attribute_param; 16 | const attribute_expiresIn = body.attribute_expiresIn; 17 | 18 | const username = event.requestContext.authorizer.claims["cognito:username"]; 19 | // keypairをdynamodbから取得する 20 | const kp = await getKeypair(username, address); 21 | const keypair = { 22 | address: kp.address.S, 23 | identifier: kp.identifier.S, 24 | publicKey: kp.publicKey.S, 25 | privateKey: kp.privateKey.S, 26 | }; 27 | const wallet = new ethers.Wallet(keypair.privateKey, provider); 28 | const chainNameOrId = (await provider.getNetwork()).chainId; 29 | 30 | const ethrDid = new EthrDID({ 31 | identifier: identifier, 32 | provider, 33 | chainNameOrId, 34 | txSigner: wallet, 35 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 36 | }); 37 | await ethrDid.setAttribute( 38 | attribute_path, 39 | attribute_param, 40 | attribute_expiresIn 41 | ); 42 | 43 | return { 44 | statusCode: 200, 45 | headers: { 46 | "Content-Type": "application/json", 47 | "Access-Control-Allow-Origin": "*", 48 | }, 49 | body: "success", 50 | }; 51 | } catch (e) { 52 | console.log(e); 53 | return { 54 | statusCode: 400, 55 | headers: { 56 | "Content-Type": "application/json", 57 | "Access-Control-Allow-Origin": "*", 58 | }, 59 | body: JSON.stringify(e), 60 | }; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/AddDelegate.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | 5 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 6 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 7 | 8 | exports.handler = async function (event) { 9 | try { 10 | const body = JSON.parse(event.body); 11 | console.log(body); 12 | const identifier = body.identifier; 13 | const address = body.signerAddress; 14 | const delegate_address = body.delegate_address; 15 | const delegate_type = body.delegate_type; 16 | const delegate_expiresIn = body.delegate_expiresIn; 17 | 18 | const username = event.requestContext.authorizer.claims["cognito:username"]; 19 | // keypairをdynamodbから取得する 20 | const kp = await getKeypair(username, address); 21 | const keypair = { 22 | address: kp.address.S, 23 | identifier: kp.identifier.S, 24 | publicKey: kp.publicKey.S, 25 | privateKey: kp.privateKey.S, 26 | }; 27 | const wallet = new ethers.Wallet(keypair.privateKey, provider); 28 | const chainNameOrId = (await provider.getNetwork()).chainId; 29 | 30 | const ethrDid = new EthrDID({ 31 | identifier: identifier, 32 | provider, 33 | chainNameOrId, 34 | txSigner: wallet, 35 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 36 | }); 37 | await ethrDid.addDelegate(delegate_address, { 38 | delegateType: delegate_type, 39 | expiresIn: delegate_expiresIn, 40 | }); 41 | 42 | return { 43 | statusCode: 200, 44 | headers: { 45 | "Content-Type": "application/json", 46 | "Access-Control-Allow-Origin": "*", 47 | }, 48 | body: "success", 49 | }; 50 | } catch (e) { 51 | console.log(e); 52 | return { 53 | statusCode: 400, 54 | headers: { 55 | "Content-Type": "application/json", 56 | "Access-Control-Allow-Origin": "*", 57 | }, 58 | body: JSON.stringify(e), 59 | }; 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/ChangeDIDOwner.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | const AWS = require("aws-sdk"); 5 | 6 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 7 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 8 | 9 | exports.handler = async function (event) { 10 | try { 11 | const body = JSON.parse(event.body); 12 | console.log(body); 13 | const identifier = body.identifier; 14 | const address = body.signerAddress; 15 | const new_owner_address = body.new_owner_address; 16 | 17 | const username = event.requestContext.authorizer.claims["cognito:username"]; 18 | // keypairをdynamodbから取得する 19 | const kp = await getKeypair(username, address); 20 | const keypair = { 21 | address: kp.address.S, 22 | identifier: kp.identifier.S, 23 | publicKey: kp.publicKey.S, 24 | privateKey: kp.privateKey.S, 25 | }; 26 | const wallet = new ethers.Wallet(keypair.privateKey, provider); 27 | const chainNameOrId = (await provider.getNetwork()).chainId; 28 | 29 | const ethrDid = new EthrDID({ 30 | identifier: identifier, 31 | provider, 32 | chainNameOrId, 33 | txSigner: wallet, 34 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 35 | }); 36 | await ethrDid.changeOwner(new_owner_address); 37 | 38 | return { 39 | statusCode: 200, 40 | headers: { 41 | "Content-Type": "application/json", 42 | "Access-Control-Allow-Origin": "*", 43 | }, 44 | body: "success", 45 | }; 46 | } catch (e) { 47 | console.log(e); 48 | return { 49 | statusCode: 400, 50 | headers: { 51 | "Content-Type": "application/json", 52 | "Access-Control-Allow-Origin": "*", 53 | }, 54 | body: JSON.stringify(e), 55 | }; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/CreateAccount.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | const ethers = require("ethers"); 3 | 4 | const tableName = process.env.TABLE_PRIVATE_KEY; 5 | const ddb = new AWS.DynamoDB(); 6 | 7 | exports.handler = async function (event) { 8 | const username = event.userName; 9 | 10 | let account = ethers.Wallet.createRandom(); 11 | 12 | const params = { 13 | TableName: tableName, 14 | Item: { 15 | id: { S: username }, 16 | key: { S: account.privateKey }, 17 | }, 18 | }; 19 | 20 | await ddb.putItem(params).promise(); 21 | 22 | return event; 23 | }; 24 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/DownloadVC.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | const { putJwt, getJwt } = require("./lib/dynamodbHelper"); 3 | const url = require("url"); 4 | const path = require("path"); 5 | const { EthrDID } = require("ethr-did"); 6 | const { Resolver } = require("did-resolver"); 7 | const ethr = require("ethr-did-resolver"); 8 | const ethers = require("ethers"); 9 | 10 | const s3 = new AWS.S3({ signatureVersion: "v4" }); 11 | const VC_BUCKET_NAME = process.env.VC_BUCKET_NAME; 12 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 13 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 14 | 15 | const headerTemplate = { 16 | "Access-Control-Allow-Headers": "Content-Type,Authorization", 17 | "Content-Type": "application/json", 18 | "Access-Control-Allow-Origin": "*", 19 | "Access-Control-Allow-Methods": "OPTIONS,GET", 20 | }; 21 | 22 | async function getSvcDownloadLink(document, svcName) { 23 | // didドキュメントからsvcのパスを抽出 24 | const documentLength = document.didDocument.service.length; 25 | for (let index = 0; index < documentLength; index++) { 26 | if ( 27 | document && 28 | document.didDocument.service && 29 | document.didDocument.service[index].type == svcName 30 | ) { 31 | console.log(document.didDocument.service[index]); 32 | // s3の保存先URLを使う。 33 | const urlParsed = url.parse( 34 | document.didDocument.service[index].serviceEndpoint.toString() 35 | ); 36 | console.log(urlParsed); 37 | console.log(urlParsed.pathname?.split(path.sep)); 38 | const username = urlParsed.pathname?.split(path.sep)[4]; 39 | const filename = urlParsed.pathname?.split(path.sep)[5]; 40 | 41 | const params = { Bucket: VC_BUCKET_NAME, Key: `${username}/${filename}` }; 42 | 43 | const downloadUrl = await s3.getSignedUrlPromise("getObject", params); 44 | console.log(downloadUrl); 45 | 46 | return downloadUrl; 47 | } 48 | } 49 | } 50 | // identifierとsvcNameを指定してコンテンツをS3からDL 51 | exports.handler = async function (event) { 52 | console.log(event.queryStringParameters); 53 | 54 | const identifier = event.queryStringParameters?.identifier; 55 | const svcName = event.queryStringParameters?.svcName; 56 | console.log(identifier); 57 | console.log(svcName); 58 | // 空欄の場合はエラーにする 59 | if (identifier == undefined || svcName == undefined) { 60 | return { 61 | statusCode: 500, 62 | headers: { 63 | "Access-Control-Allow-Headers": "Content-Type,Authorization", 64 | "Content-Type": "application/json", 65 | "Access-Control-Allow-Origin": "*", 66 | "Access-Control-Allow-Methods": "OPTIONS,GET", 67 | }, 68 | body: JSON.stringify({ 69 | error_message: "filename or identifier is undefined", 70 | }), 71 | }; 72 | } else { 73 | const chainNameOrId = (await provider.getNetwork()).chainId; 74 | 75 | const ethrDid = new EthrDID({ 76 | identifier: identifier, 77 | provider, 78 | chainNameOrId, 79 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 80 | }); 81 | 82 | const providerConfig = { 83 | rpcUrl: provider.connection.url, 84 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 85 | chainId: chainNameOrId, 86 | provider, 87 | }; 88 | const ethrResolver = ethr.getResolver(providerConfig); 89 | const didResolver = new Resolver(ethrResolver); 90 | 91 | const jwt = event.headers.Authorization || ""; 92 | console.log("jwt: ", jwt); 93 | 94 | // jwtの検証フラグ 95 | let jwt_verified = false; 96 | 97 | let payload; 98 | let issuer; 99 | let didResolutionResult; 100 | 101 | try { 102 | // jwtがdynamobdにキャッシュされているか確認 103 | let jwtCached = await getJwt(jwt); 104 | if (!jwtCached || jwtCached == {}) { 105 | // キャッシュされていない場合は、検証する 106 | const verifiedJWT = await ethrDid.verifyJWT(jwt, didResolver); 107 | console.log("verifiedJWT: ", verifiedJWT); 108 | console.log("issuer: ", verifiedJWT.issuer); 109 | console.log("payload: ", verifiedJWT.payload); 110 | console.log("didResolutionResult: ", verifiedJWT.didResolutionResult); 111 | 112 | // jwtの検証結果から使用する項目を抽出 113 | payload = verifiedJWT.payload; 114 | issuer = verifiedJWT.issuer; 115 | didResolutionResult = verifiedJWT.didResolutionResult; 116 | 117 | // 検証がOKであればjwt_verifyをtrueにする. 118 | if (verifiedJWT.verified) { 119 | jwt_verified = true; 120 | 121 | // dynamodbにjwtをキャッシュ 122 | putJwt( 123 | jwt, 124 | JSON.stringify(payload), 125 | issuer, 126 | JSON.stringify(didResolutionResult), 127 | payload?.exp || 1 128 | ); 129 | } 130 | } else { 131 | console.log("jwtCached: ", jwtCached); 132 | // キャッシュされている場合はjwt_verifiedをtrueにする 133 | jwt_verified = true; 134 | 135 | // dynamodbから使用する項目を抽出 136 | payload = JSON.parse(jwtCached.payload.S || ""); 137 | issuer = jwtCached.issuer.S || ""; 138 | didResolutionResult = JSON.parse(jwtCached.didResolutionResult.S || ""); 139 | } 140 | 141 | // didドキュメントの取得 142 | const document = didResolutionResult; 143 | console.log(document.service); 144 | 145 | // didドキュメントにsvcがなかったらエラーを返す 146 | if (!document.didDocument.service) { 147 | return { 148 | statusCode: 404, 149 | headers: { 150 | "Access-Control-Allow-Headers": "Content-Type,Authorization", 151 | "Content-Type": "application/json", 152 | "Access-Control-Allow-Origin": "*", 153 | "Access-Control-Allow-Methods": "OPTIONS,GET", 154 | }, 155 | body: JSON.stringify({ error_message: "service not found" }), 156 | }; 157 | } 158 | 159 | if (jwt_verified) { 160 | // didドキュメントからsvcのパスを抽出 161 | //const documentLength = document.didDocument.service.length 162 | const downloadUrl = await getSvcDownloadLink(document, svcName); 163 | 164 | return { 165 | statusCode: 200, 166 | headers: headerTemplate, 167 | body: JSON.stringify({ downloadUrl }), 168 | }; 169 | 170 | // } 171 | // } 172 | } else { 173 | // 認証失敗を返す 174 | return { 175 | statusCode: 401, 176 | headers: headerTemplate, 177 | body: JSON.stringify({ error_message: "jwt unverified" }), 178 | }; 179 | } 180 | } catch (error) { 181 | console.log(error); 182 | return { 183 | statusCode: 500, 184 | headers: headerTemplate, 185 | body: JSON.stringify({ 186 | error_message: "someting happen. please contact to service owner", 187 | }), 188 | }; 189 | } 190 | } 191 | }; 192 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/GenerateIdentifier.js: -------------------------------------------------------------------------------- 1 | const { putKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | const AWS = require("aws-sdk"); 5 | const ddb = new AWS.DynamoDB(); 6 | 7 | const TABLE_PRIVATE_KEY = process.env.TABLE_PRIVATE_KEY; 8 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 9 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 10 | 11 | async function getPrivateKey(username) { 12 | const paramsPrivateKey = { 13 | TableName: TABLE_PRIVATE_KEY, 14 | Key: { 15 | id: { S: username }, 16 | }, 17 | }; 18 | 19 | const res = await ddb.getItem(paramsPrivateKey).promise(); 20 | const privateKey = res.Item.key.S; 21 | return privateKey; 22 | } 23 | 24 | exports.handler = async function (event) { 25 | try { 26 | const username = event.requestContext.authorizer.claims["cognito:username"]; 27 | const kp = EthrDID.createKeyPair(); 28 | // keypairをdynamodbに記録する 29 | await putKeypair( 30 | username, 31 | kp.address, 32 | kp.identifier, 33 | kp.privateKey, 34 | kp.publicKey 35 | ); 36 | 37 | return { 38 | statusCode: 200, 39 | headers: { 40 | "Content-Type": "application/json", 41 | "Access-Control-Allow-Origin": "*", 42 | }, 43 | body: JSON.stringify({ 44 | address: kp.address, 45 | identifier: kp.identifier, 46 | publicKey: kp.publicKey, 47 | }), 48 | }; 49 | } catch (e) { 50 | console.log(e); 51 | throw new Error(e); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/GetDIDDocument.js: -------------------------------------------------------------------------------- 1 | const { EthrDID } = require("ethr-did"); 2 | const { Resolver } = require("did-resolver"); 3 | const ethr = require("ethr-did-resolver"); 4 | const ethers = require("ethers"); 5 | 6 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 7 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 8 | 9 | exports.handler = async function (event) { 10 | try { 11 | const did_url = event.queryStringParameters.did_url; 12 | 13 | const chainNameOrId = (await provider.getNetwork()).chainId; 14 | const providerConfig = { 15 | rpcUrl: provider.connection.url, 16 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 17 | chainId: chainNameOrId, 18 | provider, 19 | }; 20 | const ethrResolver = ethr.getResolver(providerConfig); 21 | const didResolver = new Resolver(ethrResolver); 22 | 23 | try { 24 | const didDocument = (await didResolver.resolve(did_url)).didDocument; 25 | console.log(didDocument); 26 | 27 | return { 28 | statusCode: 200, 29 | headers: { 30 | "Content-Type": "application/json", 31 | "Access-Control-Allow-Origin": "*", 32 | }, 33 | body: JSON.stringify({ 34 | didDocument, 35 | }), 36 | }; 37 | } catch (error) { 38 | console.log(error); 39 | return { 40 | statusCode: 403, 41 | headers: { 42 | "Content-Type": "application/json", 43 | "Access-Control-Allow-Origin": "*", 44 | }, 45 | body: JSON.stringify({ 46 | error: error, 47 | }), 48 | }; 49 | } 50 | } catch (e) { 51 | console.log(e); 52 | throw new Error(e); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/GetDIDOwner.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | const AWS = require("aws-sdk"); 5 | const ddb = new AWS.DynamoDB(); 6 | 7 | const TABLE_PRIVATE_KEY = process.env.TABLE_PRIVATE_KEY; 8 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 9 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 10 | 11 | exports.handler = async function (event) { 12 | try { 13 | const identifier = event.queryStringParameters.identifier; 14 | 15 | const chainNameOrId = (await provider.getNetwork()).chainId; 16 | 17 | const ethrDid = new EthrDID({ 18 | identifier: identifier, 19 | provider, 20 | chainNameOrId, 21 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 22 | }); 23 | const owner = await ethrDid.lookupOwner(); 24 | 25 | return { 26 | statusCode: 200, 27 | headers: { 28 | "Content-Type": "text/plain", 29 | "Access-Control-Allow-Origin": "*", 30 | }, 31 | body: owner, 32 | }; 33 | } catch (e) { 34 | console.log(e); 35 | throw new Error(e); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/GetIdentifier.js: -------------------------------------------------------------------------------- 1 | const { getKeypairs } = require("./lib/dynamodbHelper"); 2 | 3 | exports.handler = async function (event) { 4 | try { 5 | const username = event.requestContext.authorizer.claims["cognito:username"]; 6 | // keypairをdynamodbから取得する 7 | const res = await getKeypairs(username); 8 | console.log(res); 9 | 10 | return { 11 | statusCode: 200, 12 | headers: { 13 | "Content-Type": "application/json", 14 | "Access-Control-Allow-Origin": "*", 15 | }, 16 | body: JSON.stringify(res), 17 | }; 18 | } catch (e) { 19 | console.log(e); 20 | throw new Error(e); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/IssueJWT.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | 5 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 6 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 7 | 8 | exports.handler = async function (event) { 9 | try { 10 | const address = event.queryStringParameters.address; 11 | const identifier = event.queryStringParameters.identifier; 12 | 13 | const username = event.requestContext.authorizer.claims["cognito:username"]; 14 | // keypairをdynamodbから取得する 15 | const kp = await getKeypair(username, address); 16 | const keypair = { 17 | address: kp.address.S, 18 | identifier: kp.identifier.S, 19 | publicKey: kp.publicKey.S, 20 | privateKey: kp.privateKey.S, 21 | }; 22 | console.log("keypair", keypair); 23 | 24 | const chainNameOrId = (await provider.getNetwork()).chainId; 25 | 26 | const ethrDid = new EthrDID({ 27 | ...keypair, 28 | identifier: identifier, 29 | provider, 30 | chainNameOrId, 31 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 32 | }); 33 | // keypairを使ってjwtを発行する 34 | const jwt = await ethrDid.signJWT(); 35 | console.log("verification", jwt); 36 | 37 | return { 38 | statusCode: 200, 39 | headers: { 40 | "Content-Type": "text/plain", 41 | "Access-Control-Allow-Origin": "*", 42 | }, 43 | body: JSON.stringify(jwt), 44 | }; 45 | } catch (e) { 46 | console.log(e); 47 | throw new Error(e); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/RevokeAttribute.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | 5 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 6 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 7 | 8 | exports.handler = async function (event) { 9 | try { 10 | const body = JSON.parse(event.body); 11 | console.log(body); 12 | const identifier = body.identifier; 13 | const address = body.signerAddress; 14 | const attribute_path = body.attribute_path; 15 | const attribute_param = body.attribute_param; 16 | 17 | const username = event.requestContext.authorizer.claims["cognito:username"]; 18 | // keypairをdynamodbから取得する 19 | const kp = await getKeypair(username, address); 20 | const keypair = { 21 | address: kp.address.S, 22 | identifier: kp.identifier.S, 23 | publicKey: kp.publicKey.S, 24 | privateKey: kp.privateKey.S, 25 | }; 26 | const wallet = new ethers.Wallet(keypair.privateKey, provider); 27 | const chainNameOrId = (await provider.getNetwork()).chainId; 28 | 29 | const ethrDid = new EthrDID({ 30 | identifier: identifier, 31 | provider, 32 | chainNameOrId, 33 | txSigner: wallet, 34 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 35 | }); 36 | await ethrDid.revokeAttribute(attribute_path, attribute_param); 37 | 38 | return { 39 | statusCode: 200, 40 | headers: { 41 | "Content-Type": "application/json", 42 | "Access-Control-Allow-Origin": "*", 43 | }, 44 | body: "success", 45 | }; 46 | } catch (e) { 47 | console.log(e); 48 | return { 49 | statusCode: 400, 50 | headers: { 51 | "Content-Type": "application/json", 52 | "Access-Control-Allow-Origin": "*", 53 | }, 54 | body: JSON.stringify(e), 55 | }; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/RevokeDelegate.js: -------------------------------------------------------------------------------- 1 | const { getKeypair } = require("./lib/dynamodbHelper"); 2 | const { EthrDID } = require("ethr-did"); 3 | const ethers = require("ethers"); 4 | 5 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 6 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 7 | 8 | exports.handler = async function (event) { 9 | try { 10 | const body = JSON.parse(event.body); 11 | console.log(body); 12 | const identifier = body.identifier; 13 | const address = body.signerAddress; 14 | const delegate_address = body.delegate_address; 15 | const delegate_type = body.delegate_type; 16 | 17 | const username = event.requestContext.authorizer.claims["cognito:username"]; 18 | // keypairをdynamodbから取得する 19 | const kp = await getKeypair(username, address); 20 | const keypair = { 21 | address: kp.address.S, 22 | identifier: kp.identifier.S, 23 | publicKey: kp.publicKey.S, 24 | privateKey: kp.privateKey.S, 25 | }; 26 | const wallet = new ethers.Wallet(keypair.privateKey, provider); 27 | const chainNameOrId = (await provider.getNetwork()).chainId; 28 | 29 | const ethrDid = new EthrDID({ 30 | identifier: identifier, 31 | provider, 32 | chainNameOrId, 33 | txSigner: wallet, 34 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 35 | }); 36 | await ethrDid.revokeDelegate(delegate_address, delegate_type); 37 | 38 | return { 39 | statusCode: 200, 40 | headers: { 41 | "Content-Type": "application/json", 42 | "Access-Control-Allow-Origin": "*", 43 | }, 44 | body: "success", 45 | }; 46 | } catch (error) { 47 | console.log(error); 48 | return { 49 | statusCode: 400, 50 | headers: { 51 | "Content-Type": "application/json", 52 | "Access-Control-Allow-Origin": "*", 53 | }, 54 | body: JSON.stringify(error), 55 | }; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/VerifyJWT.js: -------------------------------------------------------------------------------- 1 | const { EthrDID } = require("ethr-did"); 2 | const { Resolver } = require("did-resolver"); 3 | const ethr = require("ethr-did-resolver"); 4 | const ethers = require("ethers"); 5 | const AWS = require("aws-sdk"); 6 | 7 | const ETHEREUM_ENDPOINT = process.env.ETHEREUM_ENDPOINT; 8 | const provider = new ethers.providers.JsonRpcProvider(ETHEREUM_ENDPOINT); 9 | 10 | exports.handler = async function (event) { 11 | try { 12 | const body = JSON.parse(event.body); 13 | 14 | const chainNameOrId = (await provider.getNetwork()).chainId; 15 | const providerConfig = { 16 | rpcUrl: provider.connection.url, 17 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 18 | chainId: chainNameOrId, 19 | provider, 20 | }; 21 | const ethrResolver = ethr.getResolver(providerConfig); 22 | const didResolver = new Resolver(ethrResolver); 23 | 24 | const ethrDid = new EthrDID({ 25 | identifier: body.identifier, 26 | provider, 27 | chainNameOrId, 28 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 29 | }); 30 | try { 31 | const { payload, issuer } = await ethrDid.verifyJWT( 32 | body.jwt, 33 | didResolver 34 | ); 35 | console.log("issuer: ", issuer); 36 | console.log("payload: ", payload); 37 | 38 | return { 39 | statusCode: 200, 40 | headers: { 41 | "Content-Type": "application/json", 42 | "Access-Control-Allow-Origin": "*", 43 | }, 44 | body: JSON.stringify({ 45 | issuer: issuer, 46 | payload: payload, 47 | }), 48 | }; 49 | } catch (error) { 50 | return { 51 | statusCode: 403, 52 | headers: { 53 | "Content-Type": "application/json", 54 | "Access-Control-Allow-Origin": "*", 55 | }, 56 | body: JSON.stringify({ 57 | error: error, 58 | }), 59 | }; 60 | } 61 | } catch (e) { 62 | console.log(e); 63 | throw new Error(e); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/VerifyVC.js: -------------------------------------------------------------------------------- 1 | const { 2 | Certificate, 3 | } = require("@blockcerts/cert-verifier-js/dist/verifier-node.js"); 4 | 5 | const ETHERSCAN_APIKEY = process.env.ETHERSCAN_APIKEY; 6 | 7 | exports.handler = async function (event) { 8 | try { 9 | const options = { 10 | explorerAPIs: [ 11 | { 12 | key: ETHERSCAN_APIKEY, 13 | keyPropertyName: "apikey", 14 | serviceName: "etherscan", 15 | //serviceURL: "https://goerli.etherscan.io" 16 | }, 17 | ], 18 | }; 19 | 20 | try { 21 | const certificate = new Certificate(event.body, options); 22 | await certificate.init(); 23 | const verificationResult = await certificate.verify( 24 | ({ code, label, status, errorMessage }) => { 25 | console.log("Code:", code, label, " - Status:", status); 26 | if (errorMessage) { 27 | console.log( 28 | `The step ${code} fails with the error: ${errorMessage}` 29 | ); 30 | } 31 | } 32 | ); 33 | 34 | return { 35 | statusCode: 200, 36 | headers: { 37 | "Content-Type": "application/json", 38 | "Access-Control-Allow-Origin": "*", 39 | }, 40 | body: JSON.stringify({ 41 | status: verificationResult.status, 42 | }), 43 | }; 44 | } catch (error) { 45 | return { 46 | statusCode: 403, 47 | headers: { 48 | "Content-Type": "application/json", 49 | "Access-Control-Allow-Origin": "*", 50 | }, 51 | body: JSON.stringify({ 52 | error: error, 53 | }), 54 | }; 55 | } 56 | } catch (e) { 57 | console.log(e); 58 | throw new Error(e); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/lib/dynamodbHelper.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | const TableName = process.env.TABLE_KEYPAIR || null; 3 | const SessionTableName = process.env.SESSION_TABLE_NAME || null; 4 | 5 | const ddb = new AWS.DynamoDB(); 6 | 7 | async function putKeypair( 8 | username, 9 | address, 10 | identifier, 11 | privateKey, 12 | publicKey 13 | ) { 14 | // tableNameが未定義ならエラーを返す 15 | if (!TableName) throw new Error("table name is undefined"); 16 | 17 | const params = { 18 | TableName: TableName, 19 | Item: { 20 | username: { S: username }, 21 | address: { S: address }, 22 | identifier: { S: identifier }, 23 | privateKey: { S: privateKey }, 24 | publicKey: { S: publicKey }, 25 | }, 26 | }; 27 | 28 | // jwtをdynamodbに記録 29 | await ddb.putItem(params).promise(); 30 | } 31 | 32 | async function getKeypairs(username) { 33 | // tableNameが未定義ならエラーを返す 34 | if (!TableName) throw new Error("table name is undefined"); 35 | 36 | const params = { 37 | TableName: TableName, 38 | KeyConditionExpression: "username = :Key", 39 | ExpressionAttributeValues: { 40 | ":Key": { S: username }, 41 | }, 42 | ProjectionExpression: "publicKey, address, identifier", 43 | }; 44 | 45 | const data = await ddb.query(params).promise(); 46 | // console.log(params) 47 | // console.log(data) 48 | 49 | // data.Itemが空なら{}を返す 50 | return data.Items; 51 | } 52 | 53 | async function getKeypair(username, address) { 54 | // tableNameが未定義ならエラーを返す 55 | if (!TableName) throw new Error("table name is undefined"); 56 | 57 | const params = { 58 | TableName: TableName, 59 | KeyConditionExpression: "username = :Key and address = :Address", 60 | ExpressionAttributeValues: { 61 | ":Key": { S: username }, 62 | ":Address": { S: address }, 63 | }, 64 | ProjectionExpression: "publicKey, address, identifier, privateKey", 65 | }; 66 | 67 | const data = await ddb.query(params).promise(); 68 | //console.log(params) 69 | //console.log(data) 70 | 71 | return data.Items[0]; 72 | } 73 | 74 | async function putJwt(jwt, payload, issuer, didResolutionResult, exp) { 75 | // tableNameが未定義ならエラーを返す 76 | if (!SessionTableName) throw new Error("table name is undefined"); 77 | 78 | const paramsJobStatus = { 79 | TableName: SessionTableName, 80 | Item: { 81 | jwt: { S: jwt }, 82 | payload: { S: payload }, 83 | issuer: { S: issuer }, 84 | didResolutionResult: { S: didResolutionResult }, 85 | exp: { N: exp.toString() }, 86 | }, 87 | }; 88 | 89 | // jwtをdynamodbに記録 90 | await ddb.putItem(paramsJobStatus).promise(); 91 | } 92 | 93 | async function getJwt(jwt) { 94 | // tableNameが未定義ならエラーを返す 95 | if (!SessionTableName) throw new Error("table name is undefined"); 96 | 97 | const paramsJobStatus = { 98 | TableName: SessionTableName, 99 | Key: { 100 | jwt: { S: jwt }, 101 | }, 102 | }; 103 | 104 | // jwtをdynamodbに記録 105 | const data = await ddb.getItem(paramsJobStatus).promise(); 106 | 107 | // data.Itemが空なら{}を返す 108 | return data.Item; 109 | } 110 | 111 | exports.putKeypair = putKeypair; 112 | exports.getKeypair = getKeypair; 113 | exports.getKeypairs = getKeypairs; 114 | exports.putJwt = putJwt; 115 | exports.getJwt = getJwt; 116 | -------------------------------------------------------------------------------- /cdk/lambda/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "createkeypair", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@blockcerts/cert-verifier-js": "^6.5.1", 13 | "aws-lambda": "^1.0.7", 14 | "aws-sdk": "^2.1354.0", 15 | "ethers": "^5.7.2", 16 | "ethr-did": "^2.3.6" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cdk/lambda/python/Issue_vc/conf-template.ini: -------------------------------------------------------------------------------- 1 | ## Issuerのウォレットアドレス 2 | issuing_address = 3 | ## IssuerのDIDドキュメントのリンク. json形式でウェブサーバに格納推奨。 4 | ## ethr-didの形式は未対応 5 | verification_method=did:ethr:: 6 | chain = ethereum_goerli 7 | ## goerliのrpc url. 8 | goerli_rpc_url= 9 | ## etherscanのapi token. 設定しないとレートリミットに抵触する可能性あり。 10 | etherscan_api_token= 11 | ## issuerの秘密鍵のディレクトリ。本番ではusbなど取り外しが可能なことを推奨する。 12 | usb_name=/tmp/ 13 | ## issuerの秘密鍵のパス。 14 | key_file=issuer.pk 15 | context_urls=[https://www.w3.org/2018/credentials/examples/v1] 16 | # put your unsigned certificates here for signing. Default is /data/unsigned_certificates 17 | unsigned_certificates_dir=/tmp/unsigned_certificates 18 | # final blockchain certificates output. Default is /data/unsigned_certificates 19 | blockchain_certificates_dir=/tmp/blockchain_certificates 20 | # where to store intermediate files, for debugging and checkpointing. Default is /data/work 21 | work_dir=/tmp/work 22 | signed_certificates_dir=/tmp/signed_certificates 23 | 24 | no_safe_mode 25 | -------------------------------------------------------------------------------- /cdk/lambda/python/Issue_vc/issue_vc.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from jinja2 import Template, Environment, FileSystemLoader, select_autoescape 3 | 4 | import subprocess 5 | import os 6 | import boto3 7 | import json 8 | import requests 9 | 10 | 11 | def createDirectory(path): 12 | if not os.path.exists(path): 13 | os.makedirs(path) 14 | 15 | def get_secretmanager(secret_name): 16 | 17 | url = 'http://localhost:2773' 18 | header = {'X-Aws-Parameters-Secrets-Token': os.getenv('AWS_SESSION_TOKEN')} 19 | parameter_encode = requests.utils.quote(secret_name) 20 | path = f'secretsmanager/get?secretId={parameter_encode}' 21 | res = requests.get(f'{url}/{path}', headers=header) 22 | 23 | if res.status_code == 200: 24 | secret = json.loads(res.text)["SecretString"] 25 | privateKey = json.loads(secret)['issuer_privatekey'] 26 | 27 | # 秘密鍵をファイルに保存 28 | with open("/tmp/issuer.pk", mode="w") as f: 29 | f.write(privateKey) 30 | 31 | #return data['Parameter']['Value'] 32 | return None 33 | else: 34 | print( 35 | f"Failed to get SSM parameter store {SSM_ISSUER_PRIVATEKEY_NAME}") 36 | return None 37 | 38 | # 署名前の証明書を作成.ファイルに保存する 39 | def readVerifiableCredentialTemplate(param=None): 40 | #テンプレート読み込み 41 | env = Environment( 42 | autoescape=select_autoescape(['html', 'xml']), 43 | loader=FileSystemLoader('./template', encoding='utf8') 44 | ) 45 | tmpl = env.get_template('verifiable-credential.json.j2') 46 | 47 | if not param: 48 | param = { 49 | 'id': 'xxxx', 50 | 'issuer_profile_url': os.getenv('ISSUER_PROFILE_URL'), 51 | 'name': "yamada", 52 | 'address': 'yamagata', 53 | 'phoneNumber': '111-1111-1111' 54 | } 55 | # テンプレートにVCに登録する値を設定 56 | rendered = tmpl.render(param) 57 | 58 | # json化 59 | rendered_json = json.loads(rendered) 60 | 61 | # ファイルに出力 62 | with open('/tmp/unsigned_certificates/{}.json'.format(param['id']), 'w') as f: 63 | json.dump(rendered_json, f, indent=2) 64 | 65 | return rendered_json 66 | 67 | # 生成されたVerifiableCredentialを取得する 68 | def getSignedVerifiableCredential(param=None): 69 | with open('/tmp/blockchain_certificates/{}.json'.format(param['id'])) as f: 70 | vc = json.load(f) 71 | print(vc) 72 | return vc 73 | 74 | 75 | # cert_issuerを実行して証明書を払い出す。 76 | def subprocess_cert_issuer(): 77 | args = [ 78 | 'python3', 79 | '-m', 80 | 'cert_issuer', 81 | '-c', 82 | 'conf.ini' 83 | ] 84 | output = subprocess.run(args, capture_output=True) 85 | print(output) 86 | 87 | def lambda_handler(event, context): 88 | try: 89 | #print("Received event: " + json.dumps(event, indent=2)) 90 | 91 | #print(event['body']) 92 | payload = json.loads(event['body']) 93 | print(payload) 94 | 95 | # 保存先ディレクトリの作成 96 | createDirectory('/tmp/unsigned_certificates') 97 | createDirectory('/tmp/blockchain_certificates') 98 | createDirectory('/tmp/work') 99 | createDirectory('/tmp/signed_certificates') 100 | 101 | # 秘密鍵の取得 102 | SSM_ISSUER_PRIVATEKEY_NAME = os.getenv('SSM_ISSUER_PRIVATEKEY_NAME') 103 | get_secretmanager(secret_name=SSM_ISSUER_PRIVATEKEY_NAME) 104 | 105 | ## templateからVCの署名前ファイルを生成 106 | param = { 107 | 'id': payload['id'], 108 | 'issuer_profile_url': os.environ['ISSUER_PROFILE_URL'], 109 | 'name': payload['name'], 110 | 'address': payload['address'], 111 | 'phoneNumber': payload['phoneNumber'] 112 | } 113 | readVerifiableCredentialTemplate(param) 114 | 115 | # Issuerの秘密鍵で電子署名 116 | subprocess_cert_issuer() 117 | 118 | # 生成されたVerifiable Credentialを取得 119 | vc = getSignedVerifiableCredential(param) 120 | 121 | 122 | response = { 123 | 'statusCode': 200, 124 | 'headers': { 125 | 'Content-Type': 'application/json', 126 | 'Access-Control-Allow-Origin': '*', 127 | }, 128 | 'body': json.dumps(vc) 129 | } 130 | 131 | print(response) 132 | return response 133 | 134 | except: 135 | import traceback 136 | traceback.print_exc() 137 | 138 | response = { 139 | 'statusCode': 502, 140 | 'headers': { 141 | 'Content-Type': 'application/json', 142 | 'Access-Control-Allow-Origin': '*', 143 | }, 144 | 'error': traceback.print_exc() 145 | } 146 | return response -------------------------------------------------------------------------------- /cdk/lambda/python/Issue_vc/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3==1.24.83 2 | cert-issuer==3.3.0 3 | web3<=4.4.1 4 | coincurve==17.0.0 5 | ethereum==2.3.1 6 | rlp<1 7 | eth-account<=0.3.0 8 | -------------------------------------------------------------------------------- /cdk/lambda/python/Issue_vc/template/verifiable-credential.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://www.w3.org/2018/credentials/v1", 4 | { 5 | "name": "https://schema.org/name", 6 | "address": "https://schema.org/Text", 7 | "phoneNumber": "https://schema.org/telephone" 8 | }, 9 | "https://w3id.org/blockcerts/v3" 10 | ], 11 | "id": "{{ id }}", 12 | "type": [ 13 | "VerifiableCredential", 14 | "BlockcertsCredential" 15 | ], 16 | "issuer": "{{ issuer_profile_url}}", 17 | "issuanceDate": "2022-01-01T19:33:24Z", 18 | "credentialSubject": { 19 | "name": "{{ name }}", 20 | "address": "{{ address }}", 21 | "phoneNumber": "{{ phoneNumber }}" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cdk/lib/RemoteOutputStack.ts: -------------------------------------------------------------------------------- 1 | import { RemoteOutputs } from "cdk-remote-stack"; 2 | import { Stack, StackProps } from "aws-cdk-lib"; 3 | import { Construct } from "constructs"; 4 | 5 | import { WafStack } from "./wafStack"; 6 | 7 | type Props = StackProps & { 8 | webAcl: WafStack; 9 | }; 10 | 11 | export class RemoteOutputStack extends Stack { 12 | public readonly issuerProfileWafArn: string; 13 | public readonly webappWafArn: string; 14 | 15 | constructor(scope: Construct, id: string, props: Props) { 16 | super(scope, id, props); 17 | 18 | this.addDependency(props.webAcl); 19 | const outputs = new RemoteOutputs(this, "Outputs", { stack: props.webAcl }); 20 | const issuerProfileWafArn = outputs.get("issuerProfileWafArn"); 21 | 22 | const webappWafArn = outputs.get("webappWafArn"); 23 | 24 | this.issuerProfileWafArn = issuerProfileWafArn; 25 | this.webappWafArn = webappWafArn; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cdk/lib/holderWebapp.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy } from "aws-cdk-lib"; 2 | 3 | import { Construct } from "constructs"; 4 | import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; 5 | import * as s3 from "aws-cdk-lib/aws-s3"; 6 | import * as s3Deployment from "aws-cdk-lib/aws-s3-deployment"; 7 | import * as path from "path"; 8 | 9 | import { HolderBackend } from "./resources/HolderBackend"; 10 | 11 | interface CustomProps extends StackProps { 12 | webAclArn: string; 13 | } 14 | 15 | export class HolderWebapp extends Stack { 16 | constructor(scope: Construct, id: string, props: CustomProps) { 17 | super(scope, id, props); 18 | 19 | const { webAclArn } = props; 20 | 21 | const webAppBucket = new s3.Bucket(this, "DIDDocumentHolderViewerWebApp", { 22 | websiteIndexDocument: "", 23 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 24 | encryption: s3.BucketEncryption.S3_MANAGED, 25 | enforceSSL: true, 26 | autoDeleteObjects: true, 27 | removalPolicy: RemovalPolicy.DESTROY, 28 | }); 29 | 30 | const originAccessIdentity = new cloudfront.OriginAccessIdentity( 31 | this, 32 | "AccessIdentity" 33 | ); 34 | 35 | const distribution = new cloudfront.CloudFrontWebDistribution( 36 | this, 37 | "Distribution", 38 | { 39 | originConfigs: [ 40 | { 41 | s3OriginSource: { 42 | s3BucketSource: webAppBucket, 43 | originAccessIdentity, 44 | }, 45 | behaviors: [ 46 | { 47 | isDefaultBehavior: true, 48 | }, 49 | ], 50 | }, 51 | ], 52 | errorConfigurations: [ 53 | { 54 | errorCode: 404, 55 | errorCachingMinTtl: 0, 56 | responseCode: 200, 57 | responsePagePath: "/", 58 | }, 59 | { 60 | errorCode: 403, 61 | errorCachingMinTtl: 0, 62 | responseCode: 200, 63 | responsePagePath: "/", 64 | }, 65 | ], 66 | webACLId: webAclArn, 67 | httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, 68 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 69 | geoRestriction: cloudfront.GeoRestriction.allowlist( 70 | process.env.ACCESS_FROM_REGION || "JP" 71 | ), 72 | } 73 | ); 74 | 75 | new s3Deployment.BucketDeployment(this, "Deployment", { 76 | sources: [ 77 | s3Deployment.Source.asset( 78 | path.join(__dirname, "..", "..", "holderwebapp", "dist") 79 | ), 80 | ], 81 | destinationBucket: webAppBucket, 82 | distribution, 83 | distributionPaths: ["/*"], 84 | }); 85 | 86 | new CfnOutput(this, "DIDViewerEndpointForHolder", { 87 | value: `https://${distribution.distributionDomainName}`, 88 | }); 89 | 90 | const holderBackend = new HolderBackend(this, "holderwebapp"); 91 | holderBackend.createResources(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /cdk/lib/issuerProfileBucketStack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy } from "aws-cdk-lib"; 2 | 3 | import { Construct } from "constructs"; 4 | import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; 5 | import * as cloudfront_origins from "aws-cdk-lib/aws-cloudfront-origins"; 6 | import * as s3 from "aws-cdk-lib/aws-s3"; 7 | 8 | interface CustomProps extends StackProps { 9 | webAclArn: string; 10 | } 11 | 12 | export class IssuerProfileBucketStack extends Stack { 13 | public issuerProfileBucket: s3.Bucket; 14 | public distribution: cloudfront.Distribution; 15 | 16 | constructor(scope: Construct, id: string, props: CustomProps) { 17 | super(scope, id, props); 18 | 19 | const { webAclArn } = props; 20 | 21 | this.issuerProfileBucket = new s3.Bucket(this, "IssuerProfileBucket", { 22 | websiteIndexDocument: "", 23 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 24 | encryption: s3.BucketEncryption.S3_MANAGED, 25 | enforceSSL: true, 26 | autoDeleteObjects: true, 27 | removalPolicy: RemovalPolicy.DESTROY, 28 | }); 29 | 30 | this.distribution = new cloudfront.Distribution(this, "Distribution2", { 31 | defaultBehavior: { 32 | origin: new cloudfront_origins.S3Origin(this.issuerProfileBucket), 33 | responseHeadersPolicy: 34 | cloudfront.ResponseHeadersPolicy.CORS_ALLOW_ALL_ORIGINS, 35 | }, 36 | webAclId: webAclArn, 37 | httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, 38 | geoRestriction: cloudfront.GeoRestriction.allowlist( 39 | process.env.ACCESS_FROM_REGION || "JP" 40 | ), 41 | }); 42 | 43 | new CfnOutput(this, "CloudFrontEndpoint", { 44 | value: `https://${this.distribution.distributionDomainName}`, 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cdk/lib/issuerProfileDestributionStack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps } from "aws-cdk-lib"; 2 | 3 | import { Construct } from "constructs"; 4 | import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; 5 | import * as s3 from "aws-cdk-lib/aws-s3"; 6 | import * as s3Deployment from "aws-cdk-lib/aws-s3-deployment"; 7 | import * as path from "path"; 8 | 9 | interface CustomProps extends StackProps { 10 | issuerProfileBucket: s3.Bucket; 11 | distribution: cloudfront.Distribution; 12 | } 13 | 14 | export class IssuerProfileDestributionStack extends Stack { 15 | constructor(scope: Construct, id: string, props: CustomProps) { 16 | super(scope, id, props); 17 | 18 | const { issuerProfileBucket, distribution } = props; 19 | 20 | new s3Deployment.BucketDeployment(this, "Deployment", { 21 | sources: [ 22 | s3Deployment.Source.asset(path.join(__dirname, "..", "issuerProfile")), 23 | ], 24 | destinationBucket: issuerProfileBucket, 25 | distribution, 26 | distributionPaths: ["/*"], 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cdk/lib/issuerWebapp.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy } from "aws-cdk-lib"; 2 | 3 | import { Construct } from "constructs"; 4 | import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; 5 | import * as s3 from "aws-cdk-lib/aws-s3"; 6 | import * as s3Deployment from "aws-cdk-lib/aws-s3-deployment"; 7 | import * as path from "path"; 8 | 9 | import { IssueBackend } from "./resources/IssueBackend"; 10 | 11 | interface CustomProps extends StackProps { 12 | webAclArn: string; 13 | } 14 | 15 | export class IssuerWebapp extends Stack { 16 | constructor(scope: Construct, id: string, props: CustomProps) { 17 | super(scope, id, props); 18 | 19 | const { webAclArn } = props; 20 | 21 | const webAppBucket = new s3.Bucket(this, "DIDDocumentIssuerViewerWebApp", { 22 | websiteIndexDocument: "", 23 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 24 | encryption: s3.BucketEncryption.S3_MANAGED, 25 | enforceSSL: true, 26 | autoDeleteObjects: true, 27 | removalPolicy: RemovalPolicy.DESTROY, 28 | }); 29 | 30 | const originAccessIdentity = new cloudfront.OriginAccessIdentity( 31 | this, 32 | "AccessIdentity" 33 | ); 34 | 35 | const distribution = new cloudfront.CloudFrontWebDistribution( 36 | this, 37 | "Distribution", 38 | { 39 | originConfigs: [ 40 | { 41 | s3OriginSource: { 42 | s3BucketSource: webAppBucket, 43 | originAccessIdentity, 44 | }, 45 | behaviors: [ 46 | { 47 | isDefaultBehavior: true, 48 | }, 49 | ], 50 | }, 51 | ], 52 | errorConfigurations: [ 53 | { 54 | errorCode: 404, 55 | errorCachingMinTtl: 0, 56 | responseCode: 200, 57 | responsePagePath: "/", 58 | }, 59 | { 60 | errorCode: 403, 61 | errorCachingMinTtl: 0, 62 | responseCode: 200, 63 | responsePagePath: "/", 64 | }, 65 | ], 66 | webACLId: webAclArn, 67 | httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, 68 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 69 | geoRestriction: cloudfront.GeoRestriction.allowlist( 70 | process.env.ACCESS_FROM_REGION || "JP" 71 | ), 72 | } 73 | ); 74 | 75 | new s3Deployment.BucketDeployment(this, "Deployment", { 76 | sources: [ 77 | s3Deployment.Source.asset( 78 | path.join(__dirname, "..", "..", "issuerwebapp", "dist") 79 | ), 80 | ], 81 | destinationBucket: webAppBucket, 82 | distribution, 83 | distributionPaths: ["/*"], 84 | }); 85 | 86 | new CfnOutput(this, "DIDViewerEndpointForIssuer", { 87 | value: `https://${distribution.distributionDomainName}`, 88 | }); 89 | 90 | const issueBackend = new IssueBackend(this, "issuerWebApp"); 91 | issueBackend.createResources(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /cdk/lib/resources/IssueBackend.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Stack, 3 | CfnOutput, 4 | Duration, 5 | RemovalPolicy, 6 | SecretValue, 7 | } from "aws-cdk-lib"; 8 | import { Construct } from "constructs"; 9 | import * as agw from "aws-cdk-lib/aws-apigateway"; 10 | import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager"; 11 | import * as lambda from "aws-cdk-lib/aws-lambda"; 12 | import * as lambda_python from "@aws-cdk/aws-lambda-python-alpha"; 13 | import * as logs from "aws-cdk-lib/aws-logs"; 14 | import * as iam from "aws-cdk-lib/aws-iam"; 15 | import * as cognito from "aws-cdk-lib/aws-cognito"; 16 | import * as wafv2 from "aws-cdk-lib/aws-wafv2"; 17 | import { apiWafRule } from "../../waf/apiWafRule"; 18 | 19 | export class IssueBackend extends Construct { 20 | constructor(scope: Construct, id: string) { 21 | super(scope, id); 22 | } 23 | 24 | public createResources() { 25 | // issuerがVCに署名するときに使用する秘密鍵をSecretmanagerに保存 26 | const issuerPrivatekey = new secretsmanager.Secret( 27 | this, 28 | "SecretIssuerPrivateKey", 29 | { 30 | secretObjectValue: { 31 | issuer_privatekey: SecretValue.unsafePlainText( 32 | process.env.ISSUER_PRIVATEKEY || "" 33 | ), 34 | }, 35 | } 36 | ); 37 | 38 | new CfnOutput(this, "SecretIssuerPrivateKeyArn", { 39 | value: issuerPrivatekey.secretArn, 40 | }); 41 | 42 | const userPool = new cognito.UserPool(this, "UserPool", { 43 | selfSignUpEnabled: true, 44 | signInAliases: { 45 | username: true, 46 | email: true, 47 | }, 48 | passwordPolicy: { 49 | minLength: 8, 50 | requireLowercase: true, 51 | requireUppercase: true, 52 | requireDigits: true, 53 | requireSymbols: true, 54 | }, 55 | removalPolicy: RemovalPolicy.DESTROY, 56 | }); 57 | 58 | const client = userPool.addClient("WebClient", { 59 | userPoolClientName: "webClient", 60 | idTokenValidity: Duration.days(1), 61 | accessTokenValidity: Duration.days(1), 62 | authFlows: { 63 | userPassword: true, 64 | userSrp: true, 65 | custom: true, 66 | }, 67 | }); 68 | const authorizer = new agw.CognitoUserPoolsAuthorizer(this, "Authorizer", { 69 | cognitoUserPools: [userPool], 70 | }); 71 | 72 | const defaultFuncProps = { 73 | entry: "./lambda/python/Issue_vc", 74 | handler: "lambda_handler", 75 | runtime: lambda.Runtime.PYTHON_3_9, 76 | memorySize: 256, 77 | timeout: Duration.seconds(30), 78 | tracing: lambda.Tracing.ACTIVE, 79 | logRetention: logs.RetentionDays.ONE_MONTH, 80 | }; 81 | 82 | const defaultFuncEnvironments = { 83 | ISSUER_PROFILE_URL: process.env.ISSUER_PROFILE_URL || "", 84 | SSM_ISSUER_PRIVATEKEY_NAME: issuerPrivatekey.secretName, 85 | }; 86 | 87 | const funcIssue = new lambda_python.PythonFunction(this, "funcIssueVc", { 88 | index: "issue_vc.py", 89 | layers: [ 90 | lambda_python.PythonLayerVersion.fromLayerVersionArn( 91 | this, 92 | `AWS-Parameters-and-Secrets-Lambda-Extension-layer-2`, 93 | "arn:aws:lambda:ap-northeast-1:133490724326:layer:AWS-Parameters-and-Secrets-Lambda-Extension:2" 94 | ), 95 | ], 96 | environment: { 97 | ...defaultFuncEnvironments, 98 | }, 99 | ...defaultFuncProps, 100 | }); 101 | funcIssue.addToRolePolicy( 102 | new iam.PolicyStatement({ 103 | effect: iam.Effect.ALLOW, 104 | actions: [ 105 | "secretsmanager:GetSecretValue", 106 | "secretsmanager:ListSecretVersionIds", 107 | "ssm:PutParameter", 108 | "ssm:GetParameterHistory", 109 | "ssm:GetParametersByPath", 110 | "ssm:GetParameters", 111 | "ssm:GetParameter", 112 | ], 113 | resources: [issuerPrivatekey.secretArn], 114 | }) 115 | ); 116 | 117 | //AWSマネージドルールを適用するWebACLの作成 118 | const apiWaf = new wafv2.CfnWebACL(this, "IssuerProfileWas", { 119 | defaultAction: { allow: {} }, 120 | scope: "REGIONAL", 121 | visibilityConfig: { 122 | cloudWatchMetricsEnabled: true, 123 | sampledRequestsEnabled: true, 124 | metricName: "IssuerApiWaf", 125 | }, 126 | rules: apiWafRule, 127 | }); 128 | 129 | const api = new agw.RestApi(this, "DIDIssuerApi", { 130 | defaultCorsPreflightOptions: { 131 | allowOrigins: agw.Cors.ALL_ORIGINS, 132 | allowMethods: agw.Cors.ALL_METHODS, 133 | }, 134 | }); 135 | 136 | const association = new wafv2.CfnWebACLAssociation( 137 | this, 138 | "WebAclAssociation", 139 | { 140 | resourceArn: `arn:aws:apigateway:${Stack.of(this).region}::/restapis/${ 141 | api.restApiId 142 | }/stages/${api.deploymentStage.stageName}`, 143 | webAclArn: apiWaf.attrArn, 144 | } 145 | ); 146 | association.addDependency(apiWaf); 147 | 148 | const api_issue = api.root.addResource("issue"); 149 | this.defineAPIRoute("POST", api_issue, funcIssue, authorizer); 150 | 151 | api.addGatewayResponse("Api4xx", { 152 | type: agw.ResponseType.DEFAULT_4XX, 153 | responseHeaders: { 154 | "Access-Control-Allow-Origin": "'*'", 155 | }, 156 | }); 157 | 158 | api.addGatewayResponse("Api5xx", { 159 | type: agw.ResponseType.DEFAULT_5XX, 160 | responseHeaders: { 161 | "Access-Control-Allow-Origin": "'*'", 162 | }, 163 | }); 164 | 165 | new CfnOutput(this, "UserPoolId", { 166 | value: userPool.userPoolId, 167 | exportName: "userPoolId", 168 | }); 169 | 170 | new CfnOutput(this, "UserPoolWEBClientIdForIssuerWebApp", { 171 | value: client.userPoolClientId, 172 | exportName: "userPoolWEBClientIdForIssuerWebApp", 173 | }); 174 | 175 | new CfnOutput(this, "ApiEndpointForIssuerWebApp", { 176 | value: api.url, 177 | exportName: "ApiEndpointForIssuerWebApp", 178 | }); 179 | } 180 | private defineAPIRoute( 181 | method: string, 182 | resource: agw.Resource, 183 | integration: lambda_python.PythonFunction, 184 | authorizer: agw.CognitoUserPoolsAuthorizer | null = null 185 | ): void { 186 | if (authorizer) { 187 | resource.addMethod(method, new agw.LambdaIntegration(integration), { 188 | authorizationType: agw.AuthorizationType.COGNITO, 189 | authorizer, 190 | }); 191 | } else { 192 | resource.addMethod(method, new agw.LambdaIntegration(integration)); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /cdk/lib/resources/dynamodb.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, RemovalPolicy } from "aws-cdk-lib"; 2 | import { Construct } from "constructs"; 3 | 4 | import * as dynamodb from "aws-cdk-lib/aws-dynamodb"; 5 | import * as kms from "aws-cdk-lib/aws-kms"; 6 | 7 | export class DynamodbStack { 8 | public didKeypairTable: dynamodb.Table; 9 | public jwtSessionTable: dynamodb.Table; 10 | 11 | constructor() {} 12 | 13 | public createDynamoDbForDIDKeypairTable( 14 | scope: Construct, 15 | props?: StackProps 16 | ) { 17 | const dynamoDBEncryptionKey = new kms.Key( 18 | scope, 19 | "DynamoDBEncryptionKeyForDIDKeypairTable", 20 | { 21 | enableKeyRotation: true, 22 | removalPolicy: RemovalPolicy.DESTROY, 23 | } 24 | ); 25 | 26 | this.didKeypairTable = new dynamodb.Table(scope, "DIDKeypairTable", { 27 | partitionKey: { name: "username", type: dynamodb.AttributeType.STRING }, 28 | sortKey: { name: "address", type: dynamodb.AttributeType.STRING }, 29 | //timeToLiveAttribute: 'exp', 30 | billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, 31 | encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED, 32 | encryptionKey: dynamoDBEncryptionKey, 33 | removalPolicy: RemovalPolicy.DESTROY, 34 | }); 35 | } 36 | 37 | public createDynamoDbJWTSessionTable(scope: Construct, props?: StackProps) { 38 | const dynamoDBEncryptionKey = new kms.Key( 39 | scope, 40 | "DynamoDBEncryptionKeyForJWTSessionTable", 41 | { 42 | enableKeyRotation: true, 43 | removalPolicy: RemovalPolicy.DESTROY, 44 | } 45 | ); 46 | 47 | this.jwtSessionTable = new dynamodb.Table(scope, "JWTSessionTable", { 48 | partitionKey: { name: "jwt", type: dynamodb.AttributeType.STRING }, 49 | timeToLiveAttribute: "exp", 50 | billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, 51 | encryption: dynamodb.TableEncryption.CUSTOMER_MANAGED, 52 | encryptionKey: dynamoDBEncryptionKey, 53 | removalPolicy: RemovalPolicy.DESTROY, 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cdk/lib/verifierWebapp.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput, RemovalPolicy } from "aws-cdk-lib"; 2 | 3 | import { Construct } from "constructs"; 4 | import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; 5 | import * as s3 from "aws-cdk-lib/aws-s3"; 6 | import * as s3Deployment from "aws-cdk-lib/aws-s3-deployment"; 7 | import * as path from "path"; 8 | 9 | import { VerifierBackend } from "./resources/VerifierBackend"; 10 | 11 | interface CustomProps extends StackProps { 12 | webAclArn: string; 13 | } 14 | 15 | export class VerifierWebapp extends Stack { 16 | constructor(scope: Construct, id: string, props: CustomProps) { 17 | super(scope, id, props); 18 | 19 | const { webAclArn } = props; 20 | 21 | const webAppBucket = new s3.Bucket( 22 | this, 23 | "DIDDocumentVerifierViewerWebApp", 24 | { 25 | websiteIndexDocument: "", 26 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 27 | encryption: s3.BucketEncryption.S3_MANAGED, 28 | enforceSSL: true, 29 | autoDeleteObjects: true, 30 | removalPolicy: RemovalPolicy.DESTROY, 31 | } 32 | ); 33 | 34 | const originAccessIdentity = new cloudfront.OriginAccessIdentity( 35 | this, 36 | "AccessIdentity" 37 | ); 38 | 39 | const distribution = new cloudfront.CloudFrontWebDistribution( 40 | this, 41 | "Distribution", 42 | { 43 | originConfigs: [ 44 | { 45 | s3OriginSource: { 46 | s3BucketSource: webAppBucket, 47 | originAccessIdentity, 48 | }, 49 | behaviors: [ 50 | { 51 | isDefaultBehavior: true, 52 | }, 53 | ], 54 | }, 55 | ], 56 | errorConfigurations: [ 57 | { 58 | errorCode: 404, 59 | errorCachingMinTtl: 0, 60 | responseCode: 200, 61 | responsePagePath: "/", 62 | }, 63 | { 64 | errorCode: 403, 65 | errorCachingMinTtl: 0, 66 | responseCode: 200, 67 | responsePagePath: "/", 68 | }, 69 | ], 70 | webACLId: webAclArn, 71 | httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, 72 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 73 | geoRestriction: cloudfront.GeoRestriction.allowlist( 74 | process.env.ACCESS_FROM_REGION || "JP" 75 | ), 76 | } 77 | ); 78 | 79 | new s3Deployment.BucketDeployment(this, "Deployment", { 80 | sources: [ 81 | s3Deployment.Source.asset( 82 | path.join(__dirname, "..", "..", "verifierwebapp", "dist") 83 | ), 84 | ], 85 | destinationBucket: webAppBucket, 86 | distribution, 87 | distributionPaths: ["/*"], 88 | }); 89 | 90 | new CfnOutput(this, "DIDViewerEndpointForVerifier", { 91 | value: `https://${distribution.distributionDomainName}`, 92 | }); 93 | 94 | const holderBackend = new VerifierBackend(this, "verifyWebApp"); 95 | holderBackend.createResources(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cdk/lib/wafStack.ts: -------------------------------------------------------------------------------- 1 | import { Stack, StackProps, CfnOutput } from "aws-cdk-lib"; 2 | import { Construct } from "constructs"; 3 | import * as wafv2 from "aws-cdk-lib/aws-wafv2"; 4 | import { cloudfrontWafRule } from "../waf/cloudfrontWafRule"; 5 | 6 | export class WafStack extends Stack { 7 | public readonly issuerProfileWaf: string; 8 | public readonly webappWaf: string; 9 | 10 | constructor(scope: Construct, id: string, props?: StackProps) { 11 | super(scope, id, props); 12 | 13 | const sourceIpAddress = process.env.SOURCE_IP_ADDRESS || ""; 14 | 15 | //AWSマネージドルールを適用するWebACLの作成 16 | const issuerProfileWaf = new wafv2.CfnWebACL(this, "IssuerProfileWas", { 17 | defaultAction: { allow: {} }, 18 | scope: "CLOUDFRONT", 19 | visibilityConfig: { 20 | cloudWatchMetricsEnabled: true, 21 | sampledRequestsEnabled: true, 22 | metricName: "issuerProfileWaf", 23 | }, 24 | rules: cloudfrontWafRule, 25 | }); 26 | 27 | const wafIPSet = new wafv2.CfnIPSet(this, "WafIPSet", { 28 | name: "WafIpSet", 29 | ipAddressVersion: "IPV4", 30 | scope: "CLOUDFRONT", 31 | addresses: [sourceIpAddress], 32 | }); 33 | 34 | const webappWafV2WebAcl = new wafv2.CfnWebACL(this, "webappWafV2WebAcl", { 35 | defaultAction: { allow: {} }, 36 | scope: "CLOUDFRONT", 37 | visibilityConfig: { 38 | cloudWatchMetricsEnabled: true, 39 | sampledRequestsEnabled: true, 40 | metricName: "webappWafV2WebAcl", 41 | }, 42 | rules: [ 43 | { 44 | priority: 6, 45 | name: "WafWebAclIpSetRule", 46 | action: { allow: {} }, 47 | visibilityConfig: { 48 | sampledRequestsEnabled: true, 49 | cloudWatchMetricsEnabled: true, 50 | metricName: "WafWebAclIpSetRule", 51 | }, 52 | statement: { 53 | ipSetReferenceStatement: { 54 | arn: wafIPSet.attrArn, 55 | }, 56 | }, 57 | }, 58 | ...cloudfrontWafRule, 59 | ], 60 | }); 61 | 62 | new CfnOutput(this, "cloudfrontWafV2WebAcl", { 63 | value: issuerProfileWaf.attrArn, 64 | exportName: "cloudfrontWafV2WebAcl", 65 | }); 66 | 67 | this.issuerProfileWaf = issuerProfileWaf.attrArn; 68 | this.webappWaf = webappWafV2WebAcl.attrArn; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "cdk": "bin/cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "cdk": "cdk" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "10.17.27", 14 | "@types/prettier": "2.6.0", 15 | "aws-cdk": "^2.65.0", 16 | "ts-node": "^10.9.1", 17 | "typescript": "~3.9.7" 18 | }, 19 | "dependencies": { 20 | "@aws-cdk/aws-lambda-python-alpha": "^2.56.0-alpha.0", 21 | "@aws-prototyping-sdk/pdk-nag": "^0.14.7", 22 | "aws-cdk-lib": "^2.97.1", 23 | "cdk-nag": "^2.21.86", 24 | "cdk-remote-stack": "^2.0.10", 25 | "constructs": "^10.0.0", 26 | "source-map-support": "^0.5.21" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ], 30 | "VPC_CIDER": "192.168.3.0/24" 31 | } 32 | -------------------------------------------------------------------------------- /cdk/waf/apiWafRule.ts: -------------------------------------------------------------------------------- 1 | // AWS マネージドルールのルールグループのリスト 2 | // https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-list.html 3 | export const apiWafRule = [ 4 | { 5 | name: "AWSManagedRulesCommonRuleSet", 6 | priority: 1, 7 | statement: { 8 | managedRuleGroupStatement: { 9 | vendorName: "AWS", 10 | name: "AWSManagedRulesCommonRuleSet", 11 | }, 12 | }, 13 | overrideAction: { none: {} }, 14 | visibilityConfig: { 15 | cloudWatchMetricsEnabled: true, 16 | sampledRequestsEnabled: true, 17 | metricName: "AWSManagedRulesCommonRuleSet", 18 | }, 19 | }, 20 | { 21 | name: "AWSManagedRulesAdminProtectionRuleSet", 22 | priority: 2, 23 | statement: { 24 | managedRuleGroupStatement: { 25 | vendorName: "AWS", 26 | name: "AWSManagedRulesAdminProtectionRuleSet", 27 | }, 28 | }, 29 | overrideAction: { none: {} }, 30 | visibilityConfig: { 31 | cloudWatchMetricsEnabled: true, 32 | sampledRequestsEnabled: true, 33 | metricName: "AWSManagedRulesAdminProtectionRuleSet", 34 | }, 35 | }, 36 | { 37 | name: "AWSManagedRulesKnownBadInputsRuleSet", 38 | priority: 3, 39 | statement: { 40 | managedRuleGroupStatement: { 41 | vendorName: "AWS", 42 | name: "AWSManagedRulesKnownBadInputsRuleSet", 43 | }, 44 | }, 45 | overrideAction: { none: {} }, 46 | visibilityConfig: { 47 | cloudWatchMetricsEnabled: true, 48 | sampledRequestsEnabled: true, 49 | metricName: "AWSManagedRulesKnownBadInputsRuleSet", 50 | }, 51 | }, 52 | { 53 | name: "AWSManagedRulesAmazonIpReputationList", 54 | priority: 4, 55 | statement: { 56 | managedRuleGroupStatement: { 57 | vendorName: "AWS", 58 | name: "AWSManagedRulesAmazonIpReputationList", 59 | }, 60 | }, 61 | overrideAction: { none: {} }, 62 | visibilityConfig: { 63 | cloudWatchMetricsEnabled: true, 64 | sampledRequestsEnabled: true, 65 | metricName: "AWSManagedRulesAmazonIpReputationList", 66 | }, 67 | }, 68 | { 69 | name: "AWSManagedRulesAnonymousIpList", 70 | priority: 5, 71 | statement: { 72 | managedRuleGroupStatement: { 73 | vendorName: "AWS", 74 | name: "AWSManagedRulesAnonymousIpList", 75 | }, 76 | }, 77 | overrideAction: { none: {} }, 78 | visibilityConfig: { 79 | cloudWatchMetricsEnabled: true, 80 | sampledRequestsEnabled: true, 81 | metricName: "AWSManagedRulesAnonymousIpList", 82 | }, 83 | }, 84 | ] -------------------------------------------------------------------------------- /cdk/waf/cloudfrontWafRule.ts: -------------------------------------------------------------------------------- 1 | // AWS マネージドルールのルールグループのリスト 2 | // https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-list.html 3 | export const cloudfrontWafRule = [ 4 | { 5 | name: "AWSManagedRulesCommonRuleSet", 6 | priority: 1, 7 | statement: { 8 | managedRuleGroupStatement: { 9 | vendorName: "AWS", 10 | name: "AWSManagedRulesCommonRuleSet", 11 | }, 12 | }, 13 | overrideAction: { none: {} }, 14 | visibilityConfig: { 15 | cloudWatchMetricsEnabled: true, 16 | sampledRequestsEnabled: true, 17 | metricName: "AWSManagedRulesCommonRuleSet", 18 | }, 19 | }, 20 | { 21 | name: "AWSManagedRulesAdminProtectionRuleSet", 22 | priority: 2, 23 | statement: { 24 | managedRuleGroupStatement: { 25 | vendorName: "AWS", 26 | name: "AWSManagedRulesAdminProtectionRuleSet", 27 | }, 28 | }, 29 | overrideAction: { none: {} }, 30 | visibilityConfig: { 31 | cloudWatchMetricsEnabled: true, 32 | sampledRequestsEnabled: true, 33 | metricName: "AWSManagedRulesAdminProtectionRuleSet", 34 | }, 35 | }, 36 | { 37 | name: "AWSManagedRulesKnownBadInputsRuleSet", 38 | priority: 3, 39 | statement: { 40 | managedRuleGroupStatement: { 41 | vendorName: "AWS", 42 | name: "AWSManagedRulesKnownBadInputsRuleSet", 43 | }, 44 | }, 45 | overrideAction: { none: {} }, 46 | visibilityConfig: { 47 | cloudWatchMetricsEnabled: true, 48 | sampledRequestsEnabled: true, 49 | metricName: "AWSManagedRulesKnownBadInputsRuleSet", 50 | }, 51 | }, 52 | { 53 | name: "AWSManagedRulesAmazonIpReputationList", 54 | priority: 4, 55 | statement: { 56 | managedRuleGroupStatement: { 57 | vendorName: "AWS", 58 | name: "AWSManagedRulesAmazonIpReputationList", 59 | }, 60 | }, 61 | overrideAction: { none: {} }, 62 | visibilityConfig: { 63 | cloudWatchMetricsEnabled: true, 64 | sampledRequestsEnabled: true, 65 | metricName: "AWSManagedRulesAmazonIpReputationList", 66 | }, 67 | }, 68 | { 69 | name: "AWSManagedRulesAnonymousIpList", 70 | priority: 5, 71 | statement: { 72 | managedRuleGroupStatement: { 73 | vendorName: "AWS", 74 | name: "AWSManagedRulesAnonymousIpList", 75 | }, 76 | }, 77 | overrideAction: { none: {} }, 78 | visibilityConfig: { 79 | cloudWatchMetricsEnabled: true, 80 | sampledRequestsEnabled: true, 81 | metricName: "AWSManagedRulesAnonymousIpList", 82 | }, 83 | }, 84 | ] -------------------------------------------------------------------------------- /cdk/waf/webappWafRule.ts: -------------------------------------------------------------------------------- 1 | // AWS マネージドルールのルールグループのリスト 2 | // https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-list.html 3 | export const cloudfrontWafRule = [ 4 | { 5 | name: "AWSManagedRulesCommonRuleSet", 6 | priority: 1, 7 | statement: { 8 | managedRuleGroupStatement: { 9 | vendorName: "AWS", 10 | name: "AWSManagedRulesCommonRuleSet", 11 | }, 12 | }, 13 | overrideAction: { none: {} }, 14 | visibilityConfig: { 15 | cloudWatchMetricsEnabled: true, 16 | sampledRequestsEnabled: true, 17 | metricName: "AWSManagedRulesCommonRuleSet", 18 | }, 19 | }, 20 | { 21 | name: "AWSManagedRulesAdminProtectionRuleSet", 22 | priority: 2, 23 | statement: { 24 | managedRuleGroupStatement: { 25 | vendorName: "AWS", 26 | name: "AWSManagedRulesAdminProtectionRuleSet", 27 | }, 28 | }, 29 | overrideAction: { none: {} }, 30 | visibilityConfig: { 31 | cloudWatchMetricsEnabled: true, 32 | sampledRequestsEnabled: true, 33 | metricName: "AWSManagedRulesAdminProtectionRuleSet", 34 | }, 35 | }, 36 | { 37 | name: "AWSManagedRulesKnownBadInputsRuleSet", 38 | priority: 3, 39 | statement: { 40 | managedRuleGroupStatement: { 41 | vendorName: "AWS", 42 | name: "AWSManagedRulesKnownBadInputsRuleSet", 43 | }, 44 | }, 45 | overrideAction: { none: {} }, 46 | visibilityConfig: { 47 | cloudWatchMetricsEnabled: true, 48 | sampledRequestsEnabled: true, 49 | metricName: "AWSManagedRulesKnownBadInputsRuleSet", 50 | }, 51 | }, 52 | { 53 | name: "AWSManagedRulesAmazonIpReputationList", 54 | priority: 4, 55 | statement: { 56 | managedRuleGroupStatement: { 57 | vendorName: "AWS", 58 | name: "AWSManagedRulesAmazonIpReputationList", 59 | }, 60 | }, 61 | overrideAction: { none: {} }, 62 | visibilityConfig: { 63 | cloudWatchMetricsEnabled: true, 64 | sampledRequestsEnabled: true, 65 | metricName: "AWSManagedRulesAmazonIpReputationList", 66 | }, 67 | }, 68 | { 69 | name: "AWSManagedRulesAnonymousIpList", 70 | priority: 5, 71 | statement: { 72 | managedRuleGroupStatement: { 73 | vendorName: "AWS", 74 | name: "AWSManagedRulesAnonymousIpList", 75 | }, 76 | }, 77 | overrideAction: { none: {} }, 78 | visibilityConfig: { 79 | cloudWatchMetricsEnabled: true, 80 | sampledRequestsEnabled: true, 81 | metricName: "AWSManagedRulesAnonymousIpList", 82 | }, 83 | }, 84 | ] -------------------------------------------------------------------------------- /docs/DeployEnvironment/1_CreateIssuerProfile.md: -------------------------------------------------------------------------------- 1 | Issuer Profileの作成/Create Issuer Profile 2 | === 3 | 4 | ## Issuer Profileとは/What is Issuer Profile 5 | Issuerが発行した証明書の署名を検証するために必要な情報を記録し、外部と共有するためのファイルである。Verifiable Credentialを受領したHolderやVerifierが検証時に使用する。 6 | It is a file for recording information neccesary to verify the signature of a certificate issued by issuer and sharing it with the outside world. 7 | Holders and Verifiers that have received the Verifiable Credentil are used druing verification. 8 | 9 | ## Issuerの秘密鍵と公開鍵を作成/Create Issuer's keypair 10 | 11 | - `utils` のディレクトリに含まれる`createWallet.js`を実行する. 出力結果をメモしておく。 12 | Run `createWallet.js` in `utils` folder . Please note of the output result 13 | 14 | > 秘密鍵は厳重に保管し、他人と共有しないよう注意すること. 15 | Do not share this keypair. Please save carefully. 16 | 17 | 18 | ``` 19 | $ cd utils 20 | $ npm install 21 | $ node createWallet.js 22 | { 23 | address: '0x...', 24 | privateKey: '0x...', 25 | publicKey: '0x...' 26 | } 27 | ``` 28 | 29 | - addressはブロックチェーンのウォレットアドレスとなる。証明書を発行するためにはブロックチェーンに書き込む必要があるため、ガス代と呼ばれる手数料が必要となる。このウォレットに対してガス代を支払うための暗号資産を少量入金する。 30 | address become wallet of blockchain. We need Gas for pay transaction fee when ssue Verifiable Credential.Send few crypto currency to this address for pay gas fee. 31 | 32 | - Goerliのテストネットであればこちらでテスト用の暗号資産を取得することができる。 33 | You can get gas fee of Goerli testnet at this faucet website. 34 | https://goerlifaucet.com/ 35 | 36 | ## CDKで使用するLambdaの依存パッケージを事前にインストールする/ Install dependency packages for CDK and Lambda 37 | ``` 38 | $ cd cdk/lambda/nodejs 39 | $ npm install 40 | ``` 41 | 42 | ## Issuer Profile配布用のS3とCloudFrontを作成/ Create S3 and CloudFront for publish the Issuer Profile. 43 | 44 | - アクセス可能なIPアドレスを制限するために、環境変数を設定する. 45 | Assuming it's a demo environment, set environment variables to limit the IP addresses that can be accessed. 46 | ``` 47 | $ export SOURCE_IP_ADDRESS=xxx.xxx.xxx.xxx/32 48 | ``` 49 | 50 | - アクセス可能な国を制限するために、環境変数を設定する. 51 | コンテンツの地理的ディストリビューションの制限についてはこちらを参照してください 52 | Assume that it is a verification environment and set environment variables to limit the countries that can be accessed. 53 | Please check here for restricting the geographic distribution of content 54 | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html 55 | リージョン名はISO3166-1で定義されているフォーマットです. 56 | Region name are in the format defined by ISO3166-1 57 | https://ja.wikipedia.org/wiki/ISO_3166-1 58 | 59 | ``` 60 | $ export ACCESS_FROM_REGION=JP 61 | ``` 62 | 63 | - CDKを使用してS3 BucketとCloudFrontを作成する。この時点ではS3 Bucketの中身は空の状態とする。Outputsの`CloudFrontEndpoint`をメモしておく。 64 | Deploy S3 Bucket and CloudFront with CDK. Please note of the `CloudFrontEndpoint` 65 | 66 | ``` 67 | $ cd cdk 68 | $ npm install 69 | $ cdk deploy DIDIssuerProfileBucketStack 70 | ``` 71 | 72 | ``` 73 | DIDIssuerProfileBucketStack.CloudFrontEndpoint = https://qqqqqqqqqqqqqq.cloudfront.net 74 | ``` 75 | 76 | 77 | ## Issuer Profileの作成/Create Issuer Profile 78 | 79 | - CloudFrontのエンドポイントとIssuerの公開鍵を`./issuerProfile/issuer-profile.json`に記載する。 80 | Endpoint of CloudFront and issuer's publickey, Write it in `./issuerProfile/issuer-profile.json` 81 | ``` 82 | $ cp docs/issuerProfileTemplate/issuer-profile-template.json cdk/issuerProfile/issuer-profile.json 83 | $ vi cdk/issuerProfile/issuer-profile.json 84 | ``` 85 | ``` 86 | { 87 | "@context": [ 88 | "https://w3id.org/openbadges/v2", 89 | "https://w3id.org/blockcerts/v2" 90 | ], 91 | "type": "Profile", 92 | "id": "https://qqqqqqqqqqqqqq.cloudfront.net/issuer-profile.json", 93 | "name": "Issuer Credential", 94 | "url": "https://qqqqqqqqqqqqqq.cloudfront.net", 95 | "introductionURL": "https://qqqqqqqqqqqqqq.cloudfront.net", 96 | "revocationList": "https://qqqqqqqqqqqqqq.cloudfront.net/revocation-list.json", 97 | "publicKey": [ 98 | { 99 | "id": "ecdsa-koblitz-pubkey:0xd9bb04cf7147cbcdc03052df5da64af400d710b1", 100 | "created": "2017-06-29T14:48:03.814936+00:00" 101 | } 102 | ], 103 | "email": "example@example.com" 104 | } 105 | ``` 106 | 107 | 108 | - 失効した証明書を管理するRevocation Listを作成する。 109 | Create Revocation List to manage revoked certificates. 110 | 111 | ``` 112 | $ cp docs/issuerProfileTemplate/revocation-list-template.json cdk/issuerProfile/revocation-list.json 113 | $ vi cdk/issuerProfile/revocation-list.json 114 | ``` 115 | ``` 116 | { 117 | "@context": "https://w3id.org/openbadges/v2", 118 | "id": "https://qqqqqqqqqqqqqq.cloudfront.net/revocation-list.json", 119 | "type": "RevocationList", 120 | "issuer": "https://qqqqqqqqqqqqqq.cloudfront.net/issuer-profile.json", 121 | "revokedAssertions": [] 122 | } 123 | ``` 124 | 125 | 126 | ## Issuer Profileをデプロイ/ Deploy Issuer Profile 127 | 128 | - cdkでissuer profileをS3 Bucketにデプロイ 129 | Deploy issuer profile to S3 Bucket with CDK 130 | ``` 131 | $ pwd 132 | decentralized-identity-sample/cdk 133 | 134 | $ cdk deploy DIDIssuerProfileDestributionStack 135 | ``` 136 | 137 | - ブラウザを使用してissuer profileにアクセス可能であることを確認する 138 | Access to issuer profile via your browser. 139 | 140 | ``` 141 | /issuer-profile.json 142 | ``` 143 | 144 | ![issuer profile via browser](../images/issuer_profile.png) -------------------------------------------------------------------------------- /docs/DeployEnvironment/2_DeployIssuerWebApp.md: -------------------------------------------------------------------------------- 1 | Issuer用のWebAppをデプロイ/Deploy WebApp for Issuer 2 | === 3 | 4 | ## CDKを使ったデプロイ/Deploy with CDK 5 | 6 | - issuerwebappをビルドする 7 | Build to issuer webapp 8 | 9 | ``` 10 | $ pwd 11 | decentralized-identity-sample 12 | 13 | $ cd issuerwebapp/ 14 | $ npm install 15 | $ npm run build 16 | ``` 17 | - ビルド済み資材が`issuerwebapp/dist/`に保存されたことを確認する 18 | builded package are stored in `issuerwebapp/dist/` 19 | 20 | ``` 21 | $ pwd 22 | decentralized-identity-sample/issuerwebapp 23 | 24 | $ ls dist/ 25 | css favicon.ico fonts index.html js 26 | ``` 27 | 28 | - アクセス可能なIPアドレスを制限するために、環境変数を設定する. 29 | Assuming it's a demo environment, set environment variables to limit the IP addresses that can be accessed. 30 | ``` 31 | $ export SOURCE_IP_ADDRESS=xxx.xxx.xxx.xxx/32 32 | ``` 33 | 34 | - Ethereum Nodeのエンドポイント 35 | Endpoint of Ethereum Node 36 | ``` 37 | $ export ETHEREUM_ENDPOINT=https://ETHEREUM_ENDPOINT 38 | ``` 39 | 40 | - アクセス可能な国を制限するために、環境変数を設定する. 41 | コンテンツの地理的ディストリビューションの制限についてはこちらを参照してください 42 | Assume that it is a verification environment and set environment variables to limit the countries that can be accessed. 43 | Please check here for restricting the geographic distribution of content 44 | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html 45 | リージョン名はISO3166-1で定義されているフォーマットです. 46 | Region name are in the format defined by ISO3166-1 47 | https://ja.wikipedia.org/wiki/ISO_3166-1 48 | 49 | ``` 50 | $ export ACCESS_FROM_REGION=JP 51 | ``` 52 | 53 | - issuerの秘密鍵とprofileのurlを環境変数に設定する 54 | set the issuer's private key and issuer profile url in environment variables. 55 | ``` 56 | $ export ISSUER_PRIVATEKEY=0x..... 57 | $ export ISSUER_PROFILE_URL=/issuer-profile.json 58 | ``` 59 | 60 | - issuerの設定ファイルを編集する. 設定内容は[証明書の発行](../HowToUse/IssuingVCWithCLI.md)を参照 61 | Modify issuer's config file. Please check at [Issuance of certificates](../HowToUse/IssuingVCWithCLI.md) 62 | 63 | ``` 64 | $ cd ../cdk 65 | $ pwd 66 | decentralized-identity-sample/cdk 67 | 68 | $ cp lambda/python/Issue_vc/conf-template.ini lambda/python/issue_vc/conf.ini 69 | $ vi lambda/python/issue_vc/conf.ini 70 | ``` 71 | 72 | - CDKを使用してデプロイする。出力結果はメモしておくこと 73 | Deploy with CDK. Please note of results. 74 | 75 | ``` 76 | $ pwd 77 | decentralized-identity-sample/cdk 78 | 79 | $ cdk deploy IssuerWebapp 80 | ``` 81 | ``` 82 | IssuerWebapp.ApiEndpointForIssuerWebApp = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 83 | IssuerWebapp.DIDApiEndpointE505696C = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 84 | IssuerWebapp.DIDViewerEndpointForIssuer = https://bbbbbbbbbbb.cloudfront.net 85 | IssuerWebapp.SecretIssuerPrivateKeyArn = arn:aws:secretsmanager:ap-northeast-1:00000000000:secret:SecretIssuerPrivateKey... 86 | IssuerWebapp.UserPoolId = ap-northeast-cccccccccccc 87 | IssuerWebapp.UserPoolWEBClientIdForIssuerWebApp = ddddddddddddd 88 | ``` 89 | 90 | 91 | 92 | - CDKの出力結果を使用してvuejs用の設定ファイルを作成する 93 | Modify vue.js config with CDK results. 94 | ``` 95 | $ cd ../issuerwebapp/ 96 | $ pwd 97 | decentralized-identity-sample/issuerwebapp 98 | 99 | $ vi .env.local 100 | ``` 101 | ``` 102 | VUE_APP_AWS_REGION=ap-northeast-1 103 | VUE_APP_API_ENDPOINT=https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 104 | VUE_APP_USER_POOL_ID=ap-northeast-cccccccccccc 105 | VUE_APP_USER_POOL_WEB_CLIENT_ID=ddddddddddddd 106 | ``` 107 | 108 | - 設定ファイルを反映するために再度webappをビルドする. 109 | Build the webapp again to reflect the configuration files. 110 | ``` 111 | $ pwd 112 | decentralized-identity-sample/issuerwebapp 113 | 114 | $ npm run build 115 | ``` 116 | 117 | - 再度CDKでデプロイすることで変更した設定ファイルの内容が反映される。 118 | The contents of the changed configuration file are reflected by deploying again with CDK. 119 | ``` 120 | $ cd ../cdk/ 121 | $ pwd 122 | decentralized-identity-sample/cdk 123 | 124 | $ cdk deploy IssuerWebapp 125 | ``` 126 | 127 | - ブラウザを使用して`DIDViewerEndpointForIssuer`にアクセスする. 128 | Access to `DIDViewerEndpointForIssuer` via your browser 129 | 130 | ![](../images/issuer_webapp.png) 131 | 132 | -------------------------------------------------------------------------------- /docs/DeployEnvironment/3_DeployHolderWebApp.md: -------------------------------------------------------------------------------- 1 | Holder用のWebAppをデプロイ/Deploy WebApp for Holder 2 | === 3 | 4 | ## CDKを使ったデプロイ/Deploy with CDK 5 | 6 | - holderwebappをビルドする 7 | Build holder webapp. 8 | ``` 9 | $ pwd 10 | decentralized-identity-sample 11 | 12 | $ cd holderwebapp/ 13 | $ npm install 14 | $ npm run build 15 | ``` 16 | 17 | - ビルド済み資材が`holderwebapp/dist/`に保存されたことを確認する 18 | builded package are stored in `holderwebapp/dist/` 19 | ``` 20 | $ pwd 21 | decentralized-identity-sample/holderwebapp 22 | 23 | $ ls dist/ 24 | css favicon.ico fonts index.html js 25 | ``` 26 | 27 | - クセス可能なIPアドレスを制限するために、環境変数を設定する. 28 | Assuming it's a demo environment, set environment variables to limit the IP addresses that can be accessed. 29 | ``` 30 | $ export SOURCE_IP_ADDRESS=xxx.xxx.xxx.xxx/32 31 | ``` 32 | 33 | - アクセス可能な国を制限するために、環境変数を設定する. 34 | コンテンツの地理的ディストリビューションの制限についてはこちらを参照してください 35 | Assume that it is a verification environment and set environment variables to limit the countries that can be accessed. 36 | Please check here for restricting the geographic distribution of content 37 | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html 38 | リージョン名はISO3166-1で定義されているフォーマットです. 39 | Region name are in the format defined by ISO3166-1 40 | https://ja.wikipedia.org/wiki/ISO_3166-1 41 | 42 | ``` 43 | $ export ACCESS_FROM_REGION=JP 44 | ``` 45 | 46 | - CDKを使用してデプロイする。出力結果はメモしておくこと 47 | Deploy with CDK. Plelase note of the results. 48 | 49 | ``` 50 | $ cd ../cdk/ 51 | $ pwd 52 | decentralized-identity-sample/cdk 53 | 54 | $ cdk deploy HolderWebapp 55 | ``` 56 | ``` 57 | HolderWebapp.ApiEndpointForHolderWebApp = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 58 | HolderWebapp.DIDHolderApiEndpoint2050CF0D = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 59 | HolderWebapp.DIDViewerEndpointForHolder = https://xxxxxxxxxxx.cloudfront.net 60 | HolderWebapp.HolderVcBucketName = 00000000000-holder-vc-bucket 61 | HolderWebapp.UserPoolIdForHolderWebApp = ap-northeast-cccccccccccc 62 | HolderWebapp.UserPoolWEBClientIdForHolderWebApp = ddddddddddddd 63 | ``` 64 | 65 | - CDKの出力結果を使用してvuejs用の設定ファイルを作成する 66 | Modify vue.js config with CDK results. 67 | ``` 68 | $ cd ../holderwebapp/ 69 | $ pwd 70 | decentralized-identity-sample/holderwebapp 71 | 72 | $ vi .env.local 73 | ``` 74 | ``` 75 | VUE_APP_AWS_REGION=ap-northeast-1 76 | VUE_APP_API_ENDPOINT=https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 77 | VUE_APP_USER_POOL_ID=ap-northeast-cccccccccccc 78 | VUE_APP_USER_POOL_WEB_CLIENT_ID=ddddddddddddd 79 | ``` 80 | 81 | - 設定ファイルを反映するために再度webappをビルドする 82 | Build the webapp again to reflect the configuration files. 83 | ``` 84 | $ pwd 85 | decentralized-identity-sample/holderwebapp 86 | 87 | $ npm run build 88 | ``` 89 | 90 | - 再度CDKでデプロイすることで変更した設定ファイルの内容が反映される。 91 | The contents of the changed configuration file are reflected by deploying again with CDK. 92 | ``` 93 | $ cd ../cdk/ 94 | $ pwd 95 | decentralized-identity-sample/cdk 96 | 97 | $ cdk deploy HolderWebapp 98 | ``` 99 | 100 | - ブラウザを使用して`DIDViewerEndpointForHolder`にアクセスする 101 | Access to `DIDViewerEndpointForHolder` via your browser 102 | 103 | ![](../images/holder_webapp.png) 104 | 105 | -------------------------------------------------------------------------------- /docs/DeployEnvironment/4_DeployVerifierWebApp.md: -------------------------------------------------------------------------------- 1 | Verifier用のWebAppをデプロイ/ Deploy WebApp for Verifier 2 | === 3 | 4 | ## CDKを使ったデプロイ 5 | 6 | - verifierwebappをビルドする 7 | Build verifier webapp 8 | 9 | ``` 10 | $ pwd 11 | decentralized-identity-sample 12 | 13 | $ cd verifierwebapp/ 14 | $ npm install 15 | $ npm run build 16 | ``` 17 | - ビルド済み資材が`verifierwebapp/dist/`に保存されたことを確認する 18 | builded package are stored in `verifierwebapp/dist/` 19 | ``` 20 | $ pwd 21 | decentralized-identity-sample/verifierwebapp 22 | 23 | $ ls dist/ 24 | css favicon.ico fonts index.html js 25 | ``` 26 | 27 | - アクセス可能なIPアドレスを制限するために、環境変数を設定する. 28 | Assuming it's a demo environment, set environment variables to limit the IP addresses that can be accessed. 29 | ``` 30 | $ export SOURCE_IP_ADDRESS=xxx.xxx.xxx.xxx/32 31 | ``` 32 | 33 | - Ethereum Nodeのエンドポイント 34 | Endpoint of Ethereum Node 35 | ``` 36 | $ export ETHEREUM_ENDPOINT=https://ETHEREUM_ENDPOINT 37 | ``` 38 | 39 | - アクセス可能な国を制限するために、環境変数を設定する. 40 | コンテンツの地理的ディストリビューションの制限についてはこちらを参照してください 41 | Assume that it is a verification environment and set environment variables to limit the countries that can be accessed. 42 | Please check here for restricting the geographic distribution of content 43 | https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/georestrictions.html 44 | リージョン名はISO3166-1で定義されているフォーマットです. 45 | Region name are in the format defined by ISO3166-1 46 | https://ja.wikipedia.org/wiki/ISO_3166-1 47 | 48 | ``` 49 | $ export ACCESS_FROM_REGION=JP 50 | ``` 51 | 52 | - CDKを使用してデプロイする。出力結果はメモしておくこと 53 | Deploy with CDK. Please note of the results. 54 | ``` 55 | $ cd ../cdk/ 56 | $ pwd 57 | decentralized-identity-sample/cdk 58 | 59 | $ cdk deploy VerifierWebapp 60 | ``` 61 | ``` 62 | VerifierWebapp.ApiEndpointForVerifierWebApp = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 63 | VerifierWebapp.DIDVerifierApiEndpoint3B20006C = https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 64 | VerifierWebapp.DIDViewerEndpointForVerifier = https://xxxxxxxxxxx.cloudfront.net 65 | VerifierWebapp.UserPoolIdForVerifierWebApp = ap-northeast-cccccccccccc 66 | VerifierWebapp.UserPoolWEBClientIdForVerifierWebApp = ddddddddddddd 67 | ``` 68 | 69 | - CDKの出力結果を使用してvuejs用の設定ファイルを作成する 70 | Modify vue.js config with CDK results. 71 | ``` 72 | $ cd ../verifierwebapp/ 73 | $ pwd 74 | decentralized-identity-sample/verifierwebapp 75 | 76 | $ vi .env.local 77 | ``` 78 | ``` 79 | VUE_APP_AWS_REGION=ap-northeast-1 80 | VUE_APP_API_ENDPOINT=https://aaaaaaaaa.execute-api.ap-northeast-1.amazonaws.com/prod/ 81 | VUE_APP_USER_POOL_ID=ap-northeast-cccccccccccc 82 | VUE_APP_USER_POOL_WEB_CLIENT_ID=ddddddddddddd 83 | ``` 84 | 85 | - 設定ファイルを反映するために再度webappをビルドする 86 | Build the webapp again to reflect the configuration files. 87 | ``` 88 | $ pwd 89 | decentralized-identity-sample/verifierwebapp 90 | 91 | $ npm run build 92 | ``` 93 | 94 | - 再度CDKでデプロイすることで変更した設定ファイルの内容が反映される。 95 | The contents of the changed configuration file are reflected by deploying again with CDK. 96 | ``` 97 | $ cd ../cdk/ 98 | $ pwd 99 | decentralized-identity-sample/cdk 100 | 101 | $ cdk deploy VerifierWebapp 102 | ``` 103 | 104 | - ブラウザを使用して`DIDViewerEndpointForVerifier`にアクセスする 105 | Access to `DIDViewerEndpointForVerifier` via your browser 106 | 107 | ![](../images/verifier_webapp.png) 108 | 109 | -------------------------------------------------------------------------------- /docs/HowToUse/IssuingVCWithCLI.md: -------------------------------------------------------------------------------- 1 | Verifiable Credentialの発行/Issuing Verifiable Credential 2 | === 3 | 4 | ## cert-issuerの導入/Install cert-issuer 5 | 6 | - git clone 7 | ``` 8 | $ cd ~/ 9 | $ git clone https://github.com/blockchain-certificates/cert-issuer.git && cd cert-issuer 10 | ``` 11 | 12 | - ethereum版のrequirements.txtが古いので更新する 13 | modify ethereum_requirements.txt 14 | ``` 15 | $ vi ethereum_requirements.txt 16 | web3<=4.4.1 17 | coincurve==17.0.0 18 | ethereum==2.3.1 19 | rlp<1 20 | eth-account<=0.3.0 21 | ``` 22 | 23 | - pythonの仮想環境を作成 24 | create venv for python virtual environment 25 | ``` 26 | $ python3 -m venv .venv 27 | $ source .venv/bin/activate 28 | ``` 29 | 30 | - 依存パッケージのインストール 31 | install dependencies 32 | ``` 33 | $ python3 setup.py experimental --blockchain=ethereum 34 | ``` 35 | 36 | - Issuerの秘密鍵をフォルダに保存する。 37 | Save the issuer's private key in a folder. 38 | 39 | > 本番環境では、秘密鍵はサーバーに保存せず、抜き差し可能なUSBであることが望ましい。 40 | In a production environment, the private key is not stored on a server, and it is desirable that it be a detachable USB. 41 | 42 | ``` 43 | $ vi /tmp/issuer.pk 44 | 0x..... 45 | ``` 46 | 47 | ## 発行する証明書の設定/Set the certificate to be issued 48 | 49 | - 証明書を発行するための設定ファイルを編集. 50 | Edit configuration files for issuing certificates. 51 | ``` 52 | $ vi conf.ini 53 | ``` 54 | ``` 55 | ## Issuerのウォレットアドレス/Issuer's wallet address 56 | issuing_address = 57 | verification_method= 58 | chain = ethereum_goerli 59 | ## goerliのrpc url. 60 | goerli_rpc_url= 61 | ## etherscanのapi token. 設定しないとレートリミットに抵触する可能性あり。/ etherscan api token. 62 | etherscan_api_token= 63 | ## issuerの秘密鍵のディレクトリ。本番ではusbなど取り外しが可能なことを推奨する。/diirectory of issuer's private key stored 64 | usb_name=/tmp/ 65 | ## issuerの秘密鍵のパス。/ File path of Issuer's private key 66 | key_file=issuer.pk 67 | context_urls=[https://www.w3.org/2018/credentials/examples/v1] 68 | # put your unsigned certificates here for signing. Default is /data/unsigned_certificates 69 | unsigned_certificates_dir=./data/unsigned_certificates 70 | # final blockchain certificates output. Default is /data/unsigned_certificates 71 | blockchain_certificates_dir=./data/blockchain_certificates 72 | # where to store intermediate files, for debugging and checkpointing. Default is /data/work 73 | work_dir=./data/work 74 | 75 | no_safe_mode 76 | ``` 77 | 78 | 79 | - 発行したい証明書のJSONファイルを指定のフォルダに格納する。`issuer` にissuerのprofileをアップロードしたCloudfrontのエンドポイントを記載する。 80 | Store the JSON file of the certificate you want to issue in the folder. Describe the CloudFront endpoint that uploaded the issuer profile to `issuer`. 81 | 82 | > `credentialSubject` に任意のパラメータを設定することができるが、データ構造を@contextで事前に定義する必要がある。 83 | Predefine data structures for arbitrary parameters in @context. 84 | ``` 85 | $ vi data/unsigned_certificates/verifiable-credential.json 86 | ``` 87 | ``` 88 | { 89 | "@context": [ 90 | "https://www.w3.org/2018/credentials/v1", 91 | { 92 | "hoge": "https://schema.org/Text", 93 | "value": "https://schema.org/Text" 94 | }, 95 | "https://w3id.org/blockcerts/v3" 96 | ], 97 | "id": "<この証明書のID>", 98 | "type": [ 99 | "VerifiableCredential", 100 | "BlockcertsCredential" 101 | ], 102 | "issuer": "https:///issuer-profile.json", 103 | "issuanceDate": "2022-01-01T19:33:24Z", 104 | "credentialSubject": { 105 | "id": "1393ae1e-8ec1-4dfd-aaaf-6fdda80600ae", 106 | "value": "hogehogehogehoge", 107 | "hoge": "fuga" 108 | } 109 | } 110 | ``` 111 | 112 | 113 | ## 証明書の発行/Issuance of certificates 114 | 115 | - 証明書の発行/Issuance of certificates 116 | ``` 117 | python3 -m cert_issuer -c conf.ini 118 | ``` 119 | 120 | - 発行した証明書のjsonは`./data/blockchain_certificates/`に保存される 121 | The Json file of issued certificate is stored in `./data/blockchain_certificates/` 122 | 123 | 124 | - 証明書のmerkle_jsonがTxのinput dataに記録されることで、jsonファイルに電子署名をいつ付与したかを証明することができる。 125 | Since the certificate merkle_json is recorded in Tx input data, it is possible to prove when an signature was given to the JSON file. 126 | ``` 127 | merkle_json: {'path': [], 'merkleRoot': 'f4906de7db4f9a826b5998472f51eaac762d429b29b6c2496f38870a477929d9', 'targetHash': 'f4906de7db4f9a826b5998472f51eaac762d429b29b6c2496f38870a477929d9', 'anchors': ['blink:eth:goerli:0xc7ea5d10a09b8df2e984c38ee54a603631beedbed1595b8a65f29887d9466089']} 128 | ``` 129 | 130 | - 証明書のproofValueには検証に必要な情報がbase58でエンコードされている。 131 | Information for verification is encoded in Base58. 132 | ``` 133 | "proof": { 134 | "type": "MerkleProof2019", 135 | "created": "2022-12-16T10:56:22.457644", 136 | "proofValue": "z7veGu1qoKR3AS5M3xfNxYMVGUCxFzaEQ5NkRWDGTowFPyL2gB7vtCVDfK2e4oETN19HnnqmXL3CS2qpMgnWe2XUHCVN7ufHArBc54QVVk2XouWzakWMU83iHnAsk186DuvJv5vLXN2p9bFXRcwFTfqxkyzDL9E8G8CEZ43X9HnFNz6Yz38U4ypGt6XbmKM7EnLTK5NaKRkHrQehPyRfFCFhjBEhgdSFFCJk7ouqxjdQfFRYgtKQKFLKJzvVkh49s8kXxWj92asPeD2hNUGqux9vREoakMioTi3baAQMFfeCCopyRvUtmC", 137 | "proofPurpose": "assertionMethod", 138 | "verificationMethod": "did:ethr:0x3f257ceBcD7b4Ffc892A8DeD6a5E8208daDf310D" 139 | } 140 | ``` 141 | ``` 142 | { 143 | path: [], 144 | merkleRoot: 'f4906de7db4f9a826b5998472f51eaac762d429b29b6c2496f38870a477929d9', // txのinput dataに記録されている。 145 | targetHash: 'f4906de7db4f9a826b5998472f51eaac762d429b29b6c2496f38870a477929d9', 146 | anchors: [ 147 | 'blink:eth:goerli:0x6f7c800b3254ce79c8b669e9f0ef91ddb20723c14c9ea85d04eeaf837a494317' // txHash 148 | ] 149 | } 150 | ``` 151 | -------------------------------------------------------------------------------- /docs/HowToUse/IssuingVCWithGUI.md: -------------------------------------------------------------------------------- 1 | 証明書の発行/Issuance of certificates 2 | === 3 | 4 | 5 | - ブラウザで`DIDViewerEndpointForIssuer`にアクセスする。 6 | Access to `DIDViewerEndpointForIssuer` via your browser 7 | 8 | - ログイン画面が表示されたら、`Create Account`からアカウントを作成する. 9 | When login screen is displayed, push `Create Account` button to create an account 10 | 11 | ![](./images/signup.png) 12 | 13 | - アカウント作成後にログインをする 14 | After create account, you can login. 15 | 16 | ![](./images/sigin.png) 17 | 18 | - 画面上部より`HOME`をクリックする。証明書発行画面に遷移する 19 | Move to `Home` tab 20 | 21 | - 必要事項を入力し、`VERIFIABLE CREDENTIALの発行`ボタンを押下する 22 | Enter the required information and press the `VERIFIABLE CREDENTIALの発行` button. 23 | 24 | ![](../images/issue_vc.png) 25 | 26 | - 出力された証明書をファイルに保存し、`https://www.blockcerts.org/`で検証する。 27 | Save the result of Verifiable Credential. You can verify at `https://www.blockcerts.org/` 28 | -------------------------------------------------------------------------------- /docs/VerifyVC.md: -------------------------------------------------------------------------------- 1 | 証明書の検証/Verified Credential Verification 2 | === 3 | 4 | 5 | ## プログラムでの検証/Verification with program 6 | - 環境変数の設定。etherscanのAPIを使用するため、etherscanの開発者用ページからapikeyを取得する。 7 | Set the etherscan's apikey to environment. 8 | https://etherscan.io/apis 9 | 10 | ``` 11 | $ export ETHERSCAN_APIKEY=... 12 | ``` 13 | 14 | - 依存パッケージのインストール 15 | install dependency packages 16 | ``` 17 | $ npm install @blockcerts/cert-verifier-js 18 | ``` 19 | 20 | - 検証用のプログラムを実行する。`../cert-issuer/data/blockchain_certificates/verifiable-credential.json`にある発行済みの証明書に対して検証を行う。 21 | Run the verification program. Verification Credential are stored at `../cert-issuer/data/blockchain_certificates/verifiable-credential.json` 22 | ``` 23 | $ cd utils 24 | $ node cert-verifier.js 25 | ``` 26 | 27 | - If SSL is self-certified, such as in a verification environment, run this command. 28 | When the Test environment, Set disable ssl. 29 | ``` 30 | NODE_TLS_REJECT_UNAUTHORIZED='0' node cert-verifier.js 31 | ``` 32 | 33 | 34 | ## 検証用サイトで検証/Verify on the Verification site 35 | - `../cert-issuer/data/blockchain_certificates/verifiable-credential.json`のファイルを [Blockcerts](https://www.blockcerts.org/)にアップロードすることでも検証が可能 36 | You can verify at [Blockcerts](https://www.blockcerts.org/) to upload Verifiable Credential. -------------------------------------------------------------------------------- /docs/images/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/Architecture.png -------------------------------------------------------------------------------- /docs/images/DIDの概要図.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/DIDの概要図.png -------------------------------------------------------------------------------- /docs/images/GetVCArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/GetVCArchitecture.png -------------------------------------------------------------------------------- /docs/images/holder_webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/holder_webapp.png -------------------------------------------------------------------------------- /docs/images/issue_vc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/issue_vc.png -------------------------------------------------------------------------------- /docs/images/issuer_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/issuer_profile.png -------------------------------------------------------------------------------- /docs/images/issuer_webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/issuer_webapp.png -------------------------------------------------------------------------------- /docs/images/sigin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/sigin.png -------------------------------------------------------------------------------- /docs/images/signup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/signup.png -------------------------------------------------------------------------------- /docs/images/verifier_webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/docs/images/verifier_webapp.png -------------------------------------------------------------------------------- /docs/issuerProfileTemplate/issuer-profile-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "https://w3id.org/openbadges/v2", 4 | "https://w3id.org/blockcerts/v2" 5 | ], 6 | "type": "Profile", 7 | "id": "/issuer-profile.json", 8 | "name": "Issuer Credential", 9 | "url": "https://", 10 | "introductionURL": "/intro/", 11 | "revocationList": "/revocation-list.json", 12 | "publicKey": [ 13 | { 14 | "id": "ecdsa-koblitz-pubkey:", 15 | "created": "" 16 | } 17 | ], 18 | "email": "" 19 | } -------------------------------------------------------------------------------- /docs/issuerProfileTemplate/revocation-list-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "@context": "https://w3id.org/openbadges/v2", 3 | "id": "revocation-list.json", 4 | "type": "RevocationList", 5 | "issuer": "/issuer-profile.json", 6 | "revokedAssertions": [] 7 | } -------------------------------------------------------------------------------- /docs/spec/HowVerifierGetVC.md: -------------------------------------------------------------------------------- 1 | Verifiable Credentialの取得方式/How does Verifier obtain Verifiable Credential from Holder 2 | === 3 | 4 | VerifiableCredentialはその所有者のS3 Bucketに保存されている。 5 | Verifiable Credential are stored at S3 Bucket owned by the Holder. 6 | 7 | S3 BucketはPrivateに設定されておりインターネットに公開はしていない。 8 | S3 buckets are not exposed to the internet. It's a private bucket. 9 | 10 | 所有者以外がVerifiable Credentialにアクセスをするには、S3に保存されているオブジェクトに対して参照が可能となる署名付きURLをVerifiable Credentialの所有者に発行してもらう必要がある。 11 | In order for anyone other than the Holder to access the Verifeable Credential, it is necessary to have the Holder of the Verifiably Credentials issue a signed URL that can be referenced to the object stored in S3. 12 | 13 | 14 | DID Document、Delegate、JWTの要素を組み合わせることで、DID Documentに対して権限を持つユーザーであれば、JWTの認証によって署名付きURLの発行を許可する仕組みを作ることができる。 15 | 16 | By combining the elements of a DID Document, Delegate, and JWT, it is possible to create a mechanism that permits the issuance of a signed URL by JWT authentication if the Verifier has authority over the DID Document. 17 | 18 | 19 | 20 | 21 | 22 | ![VC取得のアーキテクチャ](../images/GetVCArchitecture.png) 23 | 24 | -------------------------------------------------------------------------------- /docs/spec/Issuer_API.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | 3 | info: 4 | version: 1.0.0 5 | title: IssuerAPI 6 | description: | 7 | - Issuer用のAPI仕様書 8 | schemes: 9 | - https 10 | host: APIGATEWAY_ENDPOINT 11 | basePath: / 12 | 13 | tags: 14 | - name: "Verifiable Credential発行" 15 | description: Verifiable Credentialの発行に関するエンドポイント 16 | # エンドポイント 17 | paths: 18 | /issue: 19 | post: 20 | summary: 21 | Verifiable Credentialを発行する 22 | tags: 23 | - VC発行 24 | parameters: 25 | - name: id 26 | in: path 27 | description: 作成するVerifiableCredentialのID 28 | required: true 29 | type: "string" 30 | - name: name 31 | in: path 32 | description: 作成するVerifiableCredentialの名前 33 | required: true 34 | type: "string" 35 | - name: address 36 | in: path 37 | description: 作成するVerifiableCredentialの住所 38 | required: true 39 | type: "string" 40 | - name: phoneNumber 41 | in: path 42 | description: 作成するVerifiableCredentialの電話番号 43 | required: true 44 | type: "string" 45 | responses: 46 | 200: 47 | description: Issuerの秘密鍵で電子署名をしたVerifiable Credentialを返す 48 | schema: 49 | type: object 50 | example: [ 51 | { 52 | "@context": [ 53 | "https://www.w3.org/2018/credentials/v1", 54 | { 55 | "name": "https://schema.org/name", 56 | "address": "https://schema.org/Text", 57 | "phoneNumber": "https://schema.org/telephone" 58 | }, 59 | "https://w3id.org/blockcerts/v3" 60 | ], 61 | "id": "xxxxxxxx", 62 | "type": [ 63 | "VerifiableCredential", 64 | "BlockcertsCredential" 65 | ], 66 | "issuer": "https://xxxxxxxxxxxxxx.cloudfront.net/issuer-profile.json", 67 | "issuanceDate": "2022-01-01T19:33:24Z", 68 | "credentialSubject": { 69 | "name": "tanaka tarou", 70 | "address": "tokyo chiyoda", 71 | "phoneNumber": "000-0000-0000" 72 | }, 73 | "proof": { 74 | "type": "MerkleProof2019", 75 | "created": "2023-01-17T02:07:16.159411", 76 | "proofValue": "z7veGu1qoKR3AS5LFDufmkRBtetTpjSLHzpsQWGQQGs6AZhdcJUA2F9EjRh8ZhNBzBVpMVtkYoQgdBc1tDbLSfnPMRAzhFfJAh5iuh7ApPDfQoQzata6WD76auRKE5ukiz1yLHcNCtC9G3cjSs7z5fqAWWgE6Y3iekCvqiM5D2nKPgsy6jw8e43RFqxBQm6J1LVPFxdKH4aUtstdWCuEDv84eU36qjDvTX5P7G7JjbC2Cn2wxA1qvXZMpeGhG15Wq9nFQDDMgT76W52fP75rhSkoRWDDAYNcH8yZ4Lsvg99v71URbFzTTd", 77 | "proofPurpose": "assertionMethod", 78 | "verificationMethod": "did:ethr:0x5:0xD9bB04cF7147cBcdc03052DF5dA64af400d710b1" 79 | } 80 | } 81 | ] 82 | -------------------------------------------------------------------------------- /holderwebapp/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /holderwebapp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /holderwebapp/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /holderwebapp/README.md: -------------------------------------------------------------------------------- 1 | # issuerwebapp 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /holderwebapp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /holderwebapp/dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/holderwebapp/dist/.gitkeep -------------------------------------------------------------------------------- /holderwebapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "holderwebapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@aws-amplify/ui-vue": "^3.1.1", 12 | "@mdi/font": "5.9.55", 13 | "aws-amplify": "^5.3.12", 14 | "buffer": "^6.0.3", 15 | "core-js": "^3.8.3", 16 | "did-resolver": "^4.0.1", 17 | "ethr-did": "^2.3.6", 18 | "ethr-did-resolver": "^8.0.0", 19 | "fast-xml-parser": "^4.2.6", 20 | "path-browserify": "^1.0.1", 21 | "roboto-fontface": "*", 22 | "stream-browserify": "^3.0.0", 23 | "vue": "^3.2.13", 24 | "vue-metamask": "^2.2.1", 25 | "vue-router": "^4.0.3", 26 | "vuetify": "^3.0.0-beta.0", 27 | "webfontloader": "^1.0.0" 28 | }, 29 | "devDependencies": { 30 | "@types/webfontloader": "^1.0.0", 31 | "@typescript-eslint/eslint-plugin": "^5.4.0", 32 | "@typescript-eslint/parser": "^5.4.0", 33 | "@vue/cli-plugin-babel": "~5.0.0", 34 | "@vue/cli-plugin-eslint": "~5.0.0", 35 | "@vue/cli-plugin-router": "~5.0.0", 36 | "@vue/cli-plugin-typescript": "~5.0.0", 37 | "@vue/cli-service": "~5.0.0", 38 | "@vue/eslint-config-typescript": "^9.1.0", 39 | "eslint": "^7.32.0", 40 | "eslint-plugin-vue": "^8.0.3", 41 | "typescript": "~4.5.5", 42 | "vue-cli-plugin-vuetify": "~2.5.8", 43 | "webpack-plugin-vuetify": "^2.0.0-alpha.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /holderwebapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/holderwebapp/public/favicon.ico -------------------------------------------------------------------------------- /holderwebapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /holderwebapp/src/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | -------------------------------------------------------------------------------- /holderwebapp/src/components/AttributeDID.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /holderwebapp/src/components/ChangeDIDOwner.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 84 | 85 | -------------------------------------------------------------------------------- /holderwebapp/src/components/CheckDIDOwner.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 51 | 52 | -------------------------------------------------------------------------------- /holderwebapp/src/components/DelegateDID.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 128 | 129 | -------------------------------------------------------------------------------- /holderwebapp/src/components/GenerateIdentifier.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /holderwebapp/src/components/GetDIDDocument.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 48 | 49 | -------------------------------------------------------------------------------- /holderwebapp/src/components/GetIdentifier.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | -------------------------------------------------------------------------------- /holderwebapp/src/components/IssueJWT.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 89 | 90 | -------------------------------------------------------------------------------- /holderwebapp/src/components/ListVC.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 119 | 120 | -------------------------------------------------------------------------------- /holderwebapp/src/components/SelectKeyPair.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 39 | -------------------------------------------------------------------------------- /holderwebapp/src/components/UploadVC.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 65 | 66 | -------------------------------------------------------------------------------- /holderwebapp/src/components/VerifyJWT.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 64 | 65 | -------------------------------------------------------------------------------- /holderwebapp/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import ethr from "ethr-did-resolver"; 3 | import { Resolver } from "did-resolver"; 4 | import { EthrDID } from "ethr-did"; 5 | 6 | export async function getDidResolver(provider: Web3Provider) { 7 | const chainNameOrId = (await provider.getNetwork()).chainId; 8 | const providerConfig = { 9 | rpcUrl: provider.connection.url, 10 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 11 | chainId: chainNameOrId, 12 | provider, 13 | }; 14 | const ethrResolver = ethr.getResolver(providerConfig); 15 | const didResolver = new Resolver(ethrResolver); 16 | 17 | return didResolver; 18 | } 19 | 20 | export async function getEthrDidWithKeypair( 21 | provider: Web3Provider, 22 | keypair: any, 23 | identifier: any = null 24 | ) { 25 | const chainNameOrId = (await provider.getNetwork()).chainId; 26 | const accounts = await provider.listAccounts(); 27 | 28 | if (identifier == null) { 29 | identifier = accounts[0]; 30 | } 31 | const ethrDid = new EthrDID({ 32 | ...keypair, 33 | identifier: identifier, 34 | provider, 35 | chainNameOrId, 36 | txSigner: provider.getSigner(accounts[0]), 37 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 38 | }); 39 | return ethrDid; 40 | } 41 | 42 | export async function getEthrDidWithoutKeypair( 43 | provider: Web3Provider, 44 | identifier: any = null 45 | ) { 46 | const chainNameOrId = (await provider.getNetwork()).chainId; 47 | const accounts = await provider.listAccounts(); 48 | 49 | if (identifier == null) { 50 | identifier = accounts[0]; 51 | } 52 | console.log("identifier: ", identifier); 53 | const ethrDid = new EthrDID({ 54 | identifier: identifier, 55 | provider, 56 | chainNameOrId, 57 | txSigner: provider.getSigner(accounts[0]), 58 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 59 | }); 60 | return ethrDid; 61 | } 62 | -------------------------------------------------------------------------------- /holderwebapp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import vuetify from "./plugins/vuetify"; 5 | import { Amplify, Auth } from "aws-amplify"; 6 | 7 | import "@aws-amplify/ui-vue"; 8 | import { loadFonts } from "./plugins/webfontloader"; 9 | 10 | Amplify.configure({ 11 | API: { 12 | endpoints: [ 13 | { 14 | name: "api", 15 | endpoint: process.env.VUE_APP_API_ENDPOINT, 16 | custom_header: async () => { 17 | const currentSession = await Auth.currentSession(); 18 | if (currentSession) { 19 | return { 20 | Authorization: `Bearer ${currentSession 21 | .getIdToken() 22 | .getJwtToken()}`, 23 | }; 24 | } 25 | return {}; 26 | }, 27 | }, 28 | ], 29 | }, 30 | Auth: { 31 | region: process.env.VUE_APP_AWS_REGION, 32 | userPoolId: process.env.VUE_APP_USER_POOL_ID, 33 | userPoolWebClientId: process.env.VUE_APP_USER_POOL_WEB_CLIENT_ID, 34 | }, 35 | }); 36 | 37 | loadFonts(); 38 | 39 | createApp(App).use(router).use(vuetify).mount("#app"); 40 | -------------------------------------------------------------------------------- /holderwebapp/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | // Styles 2 | import '@mdi/font/css/materialdesignicons.css' 3 | import 'vuetify/styles' 4 | 5 | // Vuetify 6 | import { createVuetify } from 'vuetify' 7 | 8 | export default createVuetify( 9 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 10 | ) 11 | -------------------------------------------------------------------------------- /holderwebapp/src/plugins/webfontloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/webfontloader.js 3 | * 4 | * webfontloader documentation: https://github.com/typekit/webfontloader 5 | */ 6 | 7 | export async function loadFonts () { 8 | const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') 9 | 10 | webFontLoader.load({ 11 | google: { 12 | families: ['Roboto:100,300,400,500,700,900&display=swap'], 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /holderwebapp/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; 2 | import { Auth } from "aws-amplify"; 3 | 4 | import SignIn from "../views/SignIn.vue"; 5 | import HomeView from "../views/HomeView.vue"; 6 | import HolderView from "../views/HolderView.vue"; 7 | 8 | const routes: Array = [ 9 | { 10 | path: "/signin", 11 | name: "SignIn", 12 | component: SignIn, 13 | }, 14 | { 15 | path: "/", 16 | name: "home", 17 | component: HomeView, 18 | meta: { 19 | auth: true, 20 | }, 21 | }, 22 | { 23 | path: "/holder", 24 | name: "holder", 25 | component: HolderView, 26 | meta: { 27 | auth: true, 28 | }, 29 | }, 30 | ]; 31 | 32 | const router = createRouter({ 33 | history: createWebHistory(process.env.BASE_URL), 34 | routes, 35 | }); 36 | 37 | router.beforeEach(async (to, _from, next) => { 38 | if (to.matched.some((r) => r.meta.auth)) { 39 | try { 40 | const currentSession = await Auth.currentSession(); 41 | 42 | if (currentSession) { 43 | next(); 44 | } else { 45 | next({ path: "/signin" }); 46 | } 47 | } catch (e) { 48 | next({ path: "signin" }); 49 | } 50 | } else { 51 | next(); 52 | } 53 | }); 54 | 55 | export default router; 56 | -------------------------------------------------------------------------------- /holderwebapp/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /holderwebapp/src/views/HolderView.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 42 | -------------------------------------------------------------------------------- /holderwebapp/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 51 | -------------------------------------------------------------------------------- /holderwebapp/src/views/SignIn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 36 | -------------------------------------------------------------------------------- /holderwebapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "useDefineForClassFields": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /holderwebapp/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | const webpack = require('webpack'); 3 | 4 | module.exports = defineConfig({ 5 | transpileDependencies: true, 6 | configureWebpack: { 7 | devtool: 'source-map', 8 | plugins: [ 9 | // Work around for Buffer is undefined: 10 | // https://github.com/webpack/changelog-v5/issues/10 11 | new webpack.ProvidePlugin({ 12 | Buffer: ['buffer', 'Buffer'], 13 | }), 14 | new webpack.ProvidePlugin({ 15 | process: 'process/browser', 16 | }), 17 | ], 18 | resolve: { 19 | extensions: [ '.ts', '.js' ], 20 | fallback: { 21 | "stream": require.resolve("stream-browserify"), 22 | "buffer": require.resolve("buffer"), 23 | "path": require.resolve('path-browserify') 24 | } 25 | } 26 | }, 27 | pluginOptions: { 28 | vuetify: { 29 | // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /issuerwebapp/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /issuerwebapp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /issuerwebapp/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /issuerwebapp/README.md: -------------------------------------------------------------------------------- 1 | # issuerwebapp 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /issuerwebapp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /issuerwebapp/dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/issuerwebapp/dist/.gitkeep -------------------------------------------------------------------------------- /issuerwebapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issuerwebapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@aws-amplify/ui-vue": "^3.1.1", 12 | "@mdi/font": "5.9.55", 13 | "aws-amplify": "^5.3.12", 14 | "buffer": "^6.0.3", 15 | "core-js": "^3.8.3", 16 | "did-resolver": "^4.0.1", 17 | "ethr-did": "^2.3.6", 18 | "ethr-did-resolver": "^8.0.0", 19 | "path-browserify": "^1.0.1", 20 | "roboto-fontface": "*", 21 | "stream-browserify": "^3.0.0", 22 | "vue": "^3.2.13", 23 | "vue-metamask": "^2.2.1", 24 | "vue-router": "^4.0.3", 25 | "vuetify": "^3.0.0-beta.0", 26 | "webfontloader": "^1.0.0" 27 | }, 28 | "devDependencies": { 29 | "@types/webfontloader": "^1.0.0", 30 | "@typescript-eslint/eslint-plugin": "^5.4.0", 31 | "@typescript-eslint/parser": "^5.4.0", 32 | "@vue/cli-plugin-babel": "~5.0.0", 33 | "@vue/cli-plugin-eslint": "~5.0.0", 34 | "@vue/cli-plugin-router": "~5.0.0", 35 | "@vue/cli-plugin-typescript": "~5.0.0", 36 | "@vue/cli-service": "~5.0.0", 37 | "@vue/eslint-config-typescript": "^9.1.0", 38 | "eslint": "^7.32.0", 39 | "eslint-plugin-vue": "^8.0.3", 40 | "typescript": "~4.5.5", 41 | "vue-cli-plugin-vuetify": "~2.5.8", 42 | "webpack-plugin-vuetify": "^2.0.0-alpha.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /issuerwebapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/issuerwebapp/public/favicon.ico -------------------------------------------------------------------------------- /issuerwebapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /issuerwebapp/src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /issuerwebapp/src/components/IssureVC.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 80 | 81 | -------------------------------------------------------------------------------- /issuerwebapp/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import ethr from "ethr-did-resolver"; 3 | import { Resolver } from "did-resolver"; 4 | import { EthrDID } from "ethr-did"; 5 | 6 | export async function getDidResolver(provider: Web3Provider) { 7 | //const provider = new Web3Provider(window.ethereum); 8 | const chainNameOrId = (await provider.getNetwork()).chainId; 9 | const providerConfig = { 10 | rpcUrl: provider.connection.url, 11 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 12 | chainId: chainNameOrId, 13 | provider, 14 | }; 15 | const ethrResolver = ethr.getResolver(providerConfig); 16 | const didResolver = new Resolver(ethrResolver); 17 | 18 | return didResolver; 19 | } 20 | 21 | export async function getEthrDidWithKeypair( 22 | provider: Web3Provider, 23 | keypair: any, 24 | identifier: any = null 25 | ) { 26 | const chainNameOrId = (await provider.getNetwork()).chainId; 27 | const accounts = await provider.listAccounts(); 28 | 29 | if (identifier == null) { 30 | identifier = accounts[0]; 31 | } 32 | const ethrDid = new EthrDID({ 33 | ...keypair, 34 | identifier: identifier, 35 | provider, 36 | chainNameOrId, 37 | txSigner: provider.getSigner(accounts[0]), 38 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 39 | }); 40 | return ethrDid; 41 | } 42 | 43 | export async function getEthrDidWithoutKeypair( 44 | provider: Web3Provider, 45 | identifier: any = null 46 | ) { 47 | const chainNameOrId = (await provider.getNetwork()).chainId; 48 | const accounts = await provider.listAccounts(); 49 | 50 | if (identifier == null) { 51 | identifier = accounts[0]; 52 | } 53 | console.log("identifier: ", identifier); 54 | const ethrDid = new EthrDID({ 55 | identifier: identifier, 56 | provider, 57 | chainNameOrId, 58 | txSigner: provider.getSigner(accounts[0]), 59 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 60 | }); 61 | return ethrDid; 62 | } 63 | -------------------------------------------------------------------------------- /issuerwebapp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import vuetify from "./plugins/vuetify"; 5 | import { Amplify, Auth } from "aws-amplify"; 6 | 7 | import "@aws-amplify/ui-vue"; 8 | import { loadFonts } from "./plugins/webfontloader"; 9 | 10 | Amplify.configure({ 11 | API: { 12 | endpoints: [ 13 | { 14 | name: "api", 15 | endpoint: process.env.VUE_APP_API_ENDPOINT, 16 | custom_header: async () => { 17 | const currentSession = await Auth.currentSession(); 18 | if (currentSession) { 19 | return { 20 | Authorization: `Bearer ${currentSession 21 | .getIdToken() 22 | .getJwtToken()}`, 23 | }; 24 | } 25 | return {}; 26 | }, 27 | }, 28 | ], 29 | }, 30 | Auth: { 31 | region: process.env.VUE_APP_AWS_REGION, 32 | userPoolId: process.env.VUE_APP_USER_POOL_ID, 33 | userPoolWebClientId: process.env.VUE_APP_USER_POOL_WEB_CLIENT_ID, 34 | }, 35 | }); 36 | 37 | loadFonts(); 38 | 39 | createApp(App).use(router).use(vuetify).mount("#app"); 40 | -------------------------------------------------------------------------------- /issuerwebapp/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | // Styles 2 | import '@mdi/font/css/materialdesignicons.css' 3 | import 'vuetify/styles' 4 | 5 | // Vuetify 6 | import { createVuetify } from 'vuetify' 7 | 8 | export default createVuetify( 9 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 10 | ) 11 | -------------------------------------------------------------------------------- /issuerwebapp/src/plugins/webfontloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/webfontloader.js 3 | * 4 | * webfontloader documentation: https://github.com/typekit/webfontloader 5 | */ 6 | 7 | export async function loadFonts () { 8 | const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') 9 | 10 | webFontLoader.load({ 11 | google: { 12 | families: ['Roboto:100,300,400,500,700,900&display=swap'], 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /issuerwebapp/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' 2 | import { Auth } from 'aws-amplify'; 3 | 4 | import SignIn from '../views/SignIn.vue'; 5 | import IssueView from '../views/IssueView.vue' 6 | 7 | const routes: Array = [ 8 | { 9 | path: '/signin', 10 | name: 'SignIn', 11 | component: SignIn, 12 | }, 13 | { 14 | path: '/', 15 | name: 'home', 16 | component: IssueView, 17 | meta: { 18 | auth: true, 19 | }, 20 | } 21 | ] 22 | 23 | const router = createRouter({ 24 | history: createWebHistory(process.env.BASE_URL), 25 | routes 26 | }) 27 | 28 | router.beforeEach(async (to, _from, next) => { 29 | if (to.matched.some(r => r.meta.auth)) { 30 | try { 31 | const currentSession = await Auth.currentSession(); 32 | 33 | if (currentSession) { 34 | next(); 35 | } else { 36 | next({ path: '/signin' }); 37 | } 38 | } catch (e) { 39 | next({ path: 'signin' }); 40 | } 41 | } else { 42 | next(); 43 | } 44 | }); 45 | 46 | 47 | export default router 48 | -------------------------------------------------------------------------------- /issuerwebapp/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /issuerwebapp/src/views/IssueView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | -------------------------------------------------------------------------------- /issuerwebapp/src/views/SignIn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 36 | -------------------------------------------------------------------------------- /issuerwebapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "useDefineForClassFields": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /issuerwebapp/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | const webpack = require('webpack'); 3 | 4 | module.exports = defineConfig({ 5 | transpileDependencies: true, 6 | configureWebpack: { 7 | devtool: 'source-map', 8 | plugins: [ 9 | // Work around for Buffer is undefined: 10 | // https://github.com/webpack/changelog-v5/issues/10 11 | new webpack.ProvidePlugin({ 12 | Buffer: ['buffer', 'Buffer'], 13 | }), 14 | new webpack.ProvidePlugin({ 15 | process: 'process/browser', 16 | }), 17 | ], 18 | resolve: { 19 | extensions: [ '.ts', '.js' ], 20 | fallback: { 21 | "stream": require.resolve("stream-browserify"), 22 | "buffer": require.resolve("buffer"), 23 | "path": require.resolve('path-browserify') 24 | } 25 | } 26 | }, 27 | pluginOptions: { 28 | vuetify: { 29 | // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader 30 | } 31 | } 32 | }) 33 | -------------------------------------------------------------------------------- /utils/cert-verifier.js: -------------------------------------------------------------------------------- 1 | const { 2 | Certificate, 3 | } = require("@blockcerts/cert-verifier-js/dist/verifier-node.js"); 4 | var fs = require("fs"); 5 | 6 | fs.readFile( 7 | "../../cert-issuer/data/blockchain_certificates/verifiable-credential.json", 8 | "utf8", 9 | async function (err, data) { 10 | if (err) { 11 | console.log(err); 12 | } 13 | 14 | const options = { 15 | explorerAPIs: [ 16 | { 17 | key: process.env.ETHERSCAN_APIKEY, 18 | keyPropertyName: "apikey", 19 | serviceName: "etherscan", 20 | //serviceURL: "https://goerli.etherscan.io" 21 | }, 22 | ], 23 | }; 24 | 25 | let certificate = new Certificate(data, options); 26 | await certificate.init(); 27 | const verificationResult = await certificate.verify( 28 | ({ code, label, status, errorMessage }) => { 29 | console.log("Code:", code, label, " - Status:", status); 30 | if (errorMessage) { 31 | console.log(`The step ${code} fails with the error: ${errorMessage}`); 32 | } 33 | } 34 | ); 35 | 36 | if (verificationResult.status === "failure") { 37 | console.log( 38 | `The certificate is not valid. Error: ${verificationResult.errorMessage}` 39 | ); 40 | } 41 | } 42 | ); 43 | -------------------------------------------------------------------------------- /utils/createWallet.js: -------------------------------------------------------------------------------- 1 | const ethers = require("ethers"); 2 | 3 | async function main() { 4 | let randomWallet = ethers.Wallet.createRandom(); 5 | 6 | let returnParam = { 7 | address: randomWallet.address, 8 | privateKey: randomWallet.privateKey, 9 | publicKey: randomWallet.publicKey, 10 | }; 11 | 12 | console.log(returnParam); 13 | } 14 | 15 | main() 16 | .then(() => process.exit(0)) 17 | .catch((error) => { 18 | console.error(error); 19 | process.exit(1); 20 | }); 21 | -------------------------------------------------------------------------------- /utils/decodeCertProofValue.js: -------------------------------------------------------------------------------- 1 | const { Decoder } = require("@vaultie/lds-merkle-proof-2019"); 2 | const proofValueBase58 = 3 | "z7veGu1qoKR3AS5LqhP7emXYCzGSbSunhwnW4Q2B7HMPRoVyNqxMZGjEMtgKK7m1RtULZvFeMukSmJXi7ipNStWQS9vL5NjtZDcnMV7XGv1TBnwdpKqeg1ecHyzLmXddiHUJYy5eVswRcJtUa3FRyeiDbP5RoyAW9uhnCto6NELbA8QRNm3MMsSpvuu51hTsT9NCy3UqekApYewdp2WdYdXojZYDKzFtH98ULLtKfagcV37gvvbz2qaqif8zJ1SCDBz2MwQWHGhYimqxJANAspxG58m4r4YvnRG4L57oLpZoiaiwqKNgZF"; 4 | const decoder = new Decoder(proofValueBase58); 5 | console.log(decoder.decode()); 6 | -------------------------------------------------------------------------------- /utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "utils", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@blockcerts/cert-verifier-js": "^6.5.1", 13 | "@vaultie/lds-merkle-proof-2019": "^0.0.10", 14 | "ethers": "^5.7.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /verifierwebapp/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | not ie 11 5 | -------------------------------------------------------------------------------- /verifierwebapp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /verifierwebapp/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /verifierwebapp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /verifierwebapp/dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/verifierwebapp/dist/.gitkeep -------------------------------------------------------------------------------- /verifierwebapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verifierwebbapp", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@aws-amplify/ui-vue": "^3.1.1", 12 | "@mdi/font": "5.9.55", 13 | "assert": "^2.0.0", 14 | "aws-amplify": "^5.3.12", 15 | "buffer": "^6.0.3", 16 | "core-js": "^3.8.3", 17 | "crypto-browserify": "^3.12.0", 18 | "did-resolver": "^4.0.1", 19 | "ethr-did": "^2.3.6", 20 | "ethr-did-resolver": "^8.0.0", 21 | "fast-xml-parser": "^4.2.5", 22 | "https-browserify": "^1.0.0", 23 | "net": "^1.0.2", 24 | "path-browserify": "^1.0.1", 25 | "roboto-fontface": "*", 26 | "stream-browserify": "^3.0.0", 27 | "stream-http": "^3.2.0", 28 | "tls": "^0.0.1", 29 | "vue": "^3.2.13", 30 | "vue-metamask": "^2.2.1", 31 | "vue-router": "^4.0.3", 32 | "vuetify": "^3.0.0-beta.0", 33 | "webfontloader": "^1.0.0" 34 | }, 35 | "devDependencies": { 36 | "@types/webfontloader": "^1.0.0", 37 | "@typescript-eslint/eslint-plugin": "^5.4.0", 38 | "@typescript-eslint/parser": "^5.4.0", 39 | "@vue/cli-plugin-babel": "~5.0.0", 40 | "@vue/cli-plugin-eslint": "~5.0.0", 41 | "@vue/cli-plugin-router": "~5.0.0", 42 | "@vue/cli-plugin-typescript": "~5.0.0", 43 | "@vue/cli-service": "~5.0.0", 44 | "@vue/eslint-config-typescript": "^9.1.0", 45 | "eslint": "^7.32.0", 46 | "eslint-plugin-vue": "^8.0.3", 47 | "typescript": "~4.5.5", 48 | "vue-cli-plugin-vuetify": "~2.5.8", 49 | "webpack-plugin-vuetify": "^2.0.0-alpha.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /verifierwebapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/decentralized-identity-sample/7b63f28a28d3a06403e249c88838cdad6ebd7541/verifierwebapp/public/favicon.ico -------------------------------------------------------------------------------- /verifierwebapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /verifierwebapp/src/App.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 28 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/CheckDIDOwner.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 51 | 52 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/DownloadVc.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 126 | 127 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/GenerateIdentifier.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/GetDIDDocument.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 48 | 49 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/GetIdentifier.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/IssueJWT.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 89 | 90 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/ListVC.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 119 | 120 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/Metamask.vue: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/SelectKeyPair.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 39 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/UploadVC.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 65 | 66 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/VerifyJWT.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 64 | 65 | -------------------------------------------------------------------------------- /verifierwebapp/src/components/VerifyVC.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 52 | 53 | -------------------------------------------------------------------------------- /verifierwebapp/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import ethr from "ethr-did-resolver"; 3 | import { Resolver } from "did-resolver"; 4 | import { EthrDID } from "ethr-did"; 5 | 6 | export async function getDidResolver(provider: Web3Provider) { 7 | //const provider = new Web3Provider(window.ethereum); 8 | const chainNameOrId = (await provider.getNetwork()).chainId; 9 | const providerConfig = { 10 | rpcUrl: provider.connection.url, 11 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", // ERC1056のRegistry Contract Address 12 | chainId: chainNameOrId, 13 | provider, 14 | }; 15 | const ethrResolver = ethr.getResolver(providerConfig); 16 | const didResolver = new Resolver(ethrResolver); 17 | 18 | return didResolver; 19 | } 20 | 21 | export async function getEthrDidWithKeypair( 22 | provider: Web3Provider, 23 | keypair: any, 24 | identifier: any = null 25 | ) { 26 | const chainNameOrId = (await provider.getNetwork()).chainId; 27 | const accounts = await provider.listAccounts(); 28 | 29 | if (identifier == null) { 30 | identifier = accounts[0]; 31 | } 32 | const ethrDid = new EthrDID({ 33 | ...keypair, 34 | identifier: identifier, 35 | provider, 36 | chainNameOrId, 37 | txSigner: provider.getSigner(accounts[0]), 38 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 39 | }); 40 | return ethrDid; 41 | } 42 | 43 | export async function getEthrDidWithoutKeypair( 44 | provider: Web3Provider, 45 | identifier: any = null 46 | ) { 47 | const chainNameOrId = (await provider.getNetwork()).chainId; 48 | const accounts = await provider.listAccounts(); 49 | 50 | if (identifier == null) { 51 | identifier = accounts[0]; 52 | } 53 | console.log("identifier: ", identifier); 54 | const ethrDid = new EthrDID({ 55 | identifier: identifier, 56 | provider, 57 | chainNameOrId, 58 | txSigner: provider.getSigner(accounts[0]), 59 | registry: "0xdca7ef03e98e0dc2b855be647c39abe984fcf21b", 60 | }); 61 | return ethrDid; 62 | } 63 | -------------------------------------------------------------------------------- /verifierwebapp/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import vuetify from "./plugins/vuetify"; 5 | import { Amplify, Auth } from "aws-amplify"; 6 | 7 | import "@aws-amplify/ui-vue"; 8 | import { loadFonts } from "./plugins/webfontloader"; 9 | 10 | Amplify.configure({ 11 | API: { 12 | endpoints: [ 13 | { 14 | name: "api", 15 | endpoint: process.env.VUE_APP_API_ENDPOINT, 16 | custom_header: async () => { 17 | const currentSession = await Auth.currentSession(); 18 | if (currentSession) { 19 | return { 20 | Authorization: `Bearer ${currentSession 21 | .getIdToken() 22 | .getJwtToken()}`, 23 | }; 24 | } 25 | return {}; 26 | }, 27 | }, 28 | ], 29 | }, 30 | Auth: { 31 | region: process.env.VUE_APP_AWS_REGION, 32 | userPoolId: process.env.VUE_APP_USER_POOL_ID, 33 | userPoolWebClientId: process.env.VUE_APP_USER_POOL_WEB_CLIENT_ID, 34 | }, 35 | }); 36 | 37 | loadFonts(); 38 | 39 | createApp(App).use(router).use(vuetify).mount("#app"); 40 | -------------------------------------------------------------------------------- /verifierwebapp/src/plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | // Styles 2 | import '@mdi/font/css/materialdesignicons.css' 3 | import 'vuetify/styles' 4 | 5 | // Vuetify 6 | import { createVuetify } from 'vuetify' 7 | 8 | export default createVuetify( 9 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides 10 | ) 11 | -------------------------------------------------------------------------------- /verifierwebapp/src/plugins/webfontloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * plugins/webfontloader.js 3 | * 4 | * webfontloader documentation: https://github.com/typekit/webfontloader 5 | */ 6 | 7 | export async function loadFonts () { 8 | const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') 9 | 10 | webFontLoader.load({ 11 | google: { 12 | families: ['Roboto:100,300,400,500,700,900&display=swap'], 13 | }, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /verifierwebapp/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router"; 2 | import { Auth } from "aws-amplify"; 3 | 4 | import SignIn from "../views/SignIn.vue"; 5 | import HomeView from "../views/HomeView.vue"; 6 | 7 | const routes: Array = [ 8 | { 9 | path: "/signin", 10 | name: "SignIn", 11 | component: SignIn, 12 | }, 13 | { 14 | path: "/", 15 | name: "home", 16 | component: HomeView, 17 | meta: { 18 | auth: true, 19 | }, 20 | }, 21 | ]; 22 | 23 | const router = createRouter({ 24 | history: createWebHistory(process.env.BASE_URL), 25 | routes, 26 | }); 27 | 28 | router.beforeEach(async (to, _from, next) => { 29 | if (to.matched.some((r) => r.meta.auth)) { 30 | try { 31 | const currentSession = await Auth.currentSession(); 32 | 33 | if (currentSession) { 34 | next(); 35 | } else { 36 | next({ path: "/signin" }); 37 | } 38 | } catch (e) { 39 | next({ path: "signin" }); 40 | } 41 | } else { 42 | next(); 43 | } 44 | }); 45 | 46 | export default router; 47 | -------------------------------------------------------------------------------- /verifierwebapp/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /verifierwebapp/src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 46 | -------------------------------------------------------------------------------- /verifierwebapp/src/views/SignIn.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 36 | -------------------------------------------------------------------------------- /verifierwebapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "useDefineForClassFields": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /verifierwebapp/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | const webpack = require('webpack'); 3 | 4 | module.exports = defineConfig({ 5 | transpileDependencies: true, 6 | configureWebpack: { 7 | devtool: 'source-map', 8 | plugins: [ 9 | // Work around for Buffer is undefined: 10 | // https://github.com/webpack/changelog-v5/issues/10 11 | new webpack.ProvidePlugin({ 12 | Buffer: ['buffer', 'Buffer'], 13 | }), 14 | new webpack.ProvidePlugin({ 15 | process: 'process/browser', 16 | }), 17 | ], 18 | resolve: { 19 | extensions: [ '.ts', '.js' ], 20 | fallback: { 21 | "stream": require.resolve("stream-browserify"), 22 | "buffer": require.resolve("buffer"), 23 | "path": require.resolve('path-browserify'), 24 | } 25 | } 26 | }, 27 | pluginOptions: { 28 | vuetify: { 29 | // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader 30 | } 31 | } 32 | }) 33 | --------------------------------------------------------------------------------