├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── did-methods └── https-did-method.md ├── flows ├── generic.plantuml ├── generic.png ├── generic.svg ├── identityverification.plantuml ├── identityverification.png ├── identityverification.svg ├── index.md ├── personalsign.md ├── personalsign.png ├── personalsign.svg ├── privatechain.md ├── privatechain.plantuml ├── privatechain.png ├── privatechain.svg ├── pushflow.plantuml ├── pushflow.png ├── pushflow.svg ├── selectivedisclosure.md ├── selectivedisclosure.plantuml ├── selectivedisclosure.png ├── selectivedisclosure.svg ├── tx.md ├── tx.plantuml ├── tx.png ├── tx.svg ├── typeddatasig.md ├── typeddatasig.png ├── typeddatasig.svg ├── verification.md ├── verification.plantuml ├── verification.png ├── verification.svg ├── verificationreq.md ├── verificationreq.plantuml ├── verificationreq.png └── verificationreq.svg ├── messages ├── claims.md ├── encryption.md ├── index.md ├── personalsignreq.md ├── personalsignresp.md ├── sharereq.md ├── shareresp.md ├── signtypeddatareq.md ├── signtypeddataresp.md ├── tx.md ├── verification.md └── verificationreq.md ├── pki ├── diddocument.md ├── identitydocument.md ├── index.md ├── jwtflow.plantuml ├── jwtflow.png ├── jwtflow.svg ├── mobileregistration.plantuml ├── mobileregistration.png ├── mobileregistration.svg ├── registration.plantuml ├── registration.png ├── registration.svg ├── resolve.plantuml ├── resolve.png └── resolve.svg ├── rest-apis ├── fuel-server.md ├── relay-server.md └── request-server.md ├── transports ├── desktopdapp.plantuml ├── desktopdapp.png ├── desktopdapp.svg ├── desktopserverapp.plantuml ├── desktopserverapp.png ├── desktopserverapp.svg ├── index.md ├── mobile.plantuml ├── mobile.png ├── mobile.svg ├── push.md ├── push.plantuml ├── push.png └── push.svg └── uportskin.plantuml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | plantuml.jar 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # adapted from https://gist.github.com/hjst/4f2f2c2ca9bd550e50c7f06cb17775b2 2 | 3 | PLANTUML_JAR_URL = https://sourceforge.net/projects/plantuml/files/plantuml.jar/download 4 | DIAGRAMS_SRC := $(wildcard **/*.plantuml) 5 | DIAGRAMS_PNG := $(addsuffix .png, $(basename $(DIAGRAMS_SRC))) 6 | DIAGRAMS_SVG := $(addsuffix .svg, $(basename $(DIAGRAMS_SRC))) 7 | 8 | all: png svg 9 | 10 | # build PNGs, probably what we want most of the time 11 | png: plantuml.jar $(DIAGRAMS_PNG) 12 | 13 | # SVG are nice-to-have but don't need to build by default 14 | svg: plantuml.jar $(DIAGRAMS_SVG) 15 | 16 | # clean up compiled files 17 | clean: 18 | rm -f plantuml.jar $(DIAGRAMS_PNG) $(DIAGRAMS_SVG) 19 | 20 | # If the JAR file isn't already present, download it 21 | plantuml.jar: 22 | curl -sSfL $(PLANTUML_JAR_URL) -o plantuml.jar 23 | 24 | # Each PNG output depends on its corresponding .plantuml file 25 | **/%.png: **/%.plantuml 26 | java -jar plantuml.jar -tpng $^ 27 | 28 | # Each SVG output depends on its corresponding .plantuml file 29 | **/%.svg: **/%.plantuml 30 | java -jar plantuml.jar -tsvg $^ 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Uport Specs" 3 | index: 3 4 | category: "specs" 5 | type: "reference" 6 | source: "https://github.com/uport-project/specs/blob/develop/README.md" 7 | --- 8 | 9 | # uPort Specs 10 | 11 | uPort is a platform for user centric identity and communication. The platform currently consists of our mobile app, Ethereum smart contracts and number of open protocols for signed messages and message flow. 12 | 13 | ## Identities 14 | 15 | An identity in uPort is really just someone or something that can sign data or transactions and also receive signed data about itself. 16 | 17 | An identity has: 18 | 19 | - An Identifier in the form of a [Decentralized ID (DID)](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) 20 | - A signing key 21 | - A public key stored on the [uPort Registry](https://github.com/uport-project/uport-registry) 22 | 23 | An identity can: 24 | 25 | - Sign JWTs (JSON Web Tokens) 26 | - [Authenticate themselves to a third party](messages/shareresp.md) 27 | - [Disclose private information about themselves](messages/shareresp.md) 28 | - [Receive requests for disclosure about themselves](messages/sharereq.md) 29 | - [Receive and store signed third party verifications about themselves](flows/verification.md) 30 | - [Sign Ethereum transactions](flows/tx.md) 31 | 32 | ### Identities created using the uPort Mobile App 33 | 34 | Currently most uPort users manage their identities through our mobile app. Identities created today consist of an instance of the [Proxy](https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol) smart contract deployed on a supported Ethereum-compatible blockchain. 35 | 36 | ## Request Flows 37 | 38 | A request will typically be signed by a client app and sent to mobile app using this generic request flow: 39 | 40 | ![Generic uPort Request Flow](flows/generic.png) 41 | 42 | We currently support the following flows: 43 | 44 | - [Selective Disclosure Flow](flows/selectivedisclosure.md) 45 | - [Send Verification Flow](flows/verification.md) 46 | - [Ethereum Transaction Request Flow](flows/tx.md) 47 | - [Private Chain Flow](flows/privatechain.md) 48 | 49 | ### [More about request flows](flows/index.md) 50 | 51 | ## Request and Response Transports 52 | 53 | There are various ways that requests can be sent to the uPort app and how responses can be returned. 54 | 55 | ### [Request/Response Transports](transports/index.md) 56 | 57 | ## Off-chain Messages 58 | 59 | Most request and responses are performed privately off-chain between the different parties to a flow. 60 | 61 | Most off-chain messages consist of signed JWTs (JSON Web Tokens) as defined in [RFC 7519](https://tools.ietf.org/html/rfc7519). Signatures are verified using our simple uPort PKI (see later in this document). 62 | 63 | ### [More about Off-chain Messages](messages/index.md) 64 | 65 | ## On-chain Transactions 66 | 67 | The user can sign Ethereum transactions using the mobile app. 68 | 69 | ### [More about Signing Ethereum transactions](flows/tx.md) 70 | 71 | ## uPort PKI 72 | 73 | uPort implements a simple yet general purpose decentralized PKI system making it easy to create and verify off-chain JWT messages. 74 | 75 | ### [More about the uPort PKI](pki/index.md) 76 | -------------------------------------------------------------------------------- /did-methods/https-did-method.md: -------------------------------------------------------------------------------- 1 | # HTTPS DID Method Specification 2 | 3 | ## Author 4 | 5 | - uPort Team 6 | - Mike Xu 7 | - Oliver Terbu 8 | 9 | ## Preface 10 | 11 | The HTTPS DID method specification conforms to the requirements specified in the 12 | [DID specification](https://w3c-ccg.github.io/did-spec/), currently published by the W3C Credentials Community Group. 13 | For more information about DIDs and DID method specifications, please see the 14 | [DID Primer](https://github.com/WebOfTrustInfo/rebooting-the-web-of-trust-fall2017/blob/master/topics-and-advance-readings/did-primer.md) 15 | 16 | ## Abstract 17 | 18 | DIDs that target a distributed ledger face significant practical challenges in bootstrapping enough meaningful 19 | trusted data around identities to incentivize mass adoption. We propose using a new DID method in conjunction 20 | with blockchain-based DIDs that allows them to bootstrap trust using a web domain's existing reputation. 21 | 22 | ## Example 23 | 24 | ```json 25 | { 26 | "@context": "https://w3id.org/did/v1", 27 | "id": "did:https:example.com", 28 | "publicKey": [{ 29 | "id": "did:https:example.com#owner", 30 | "type": "Secp256k1VerificationKey2018", 31 | "owner": "did:https:example.com", 32 | "ethereumAddress": "0xb9c5714089478a327f09197987f16f9e5d936e8a" 33 | }], 34 | "authentication": [{ 35 | "type": "Secp256k1SignatureAuthentication2018", 36 | "publicKey": "did:https:example.com#owner" 37 | }] 38 | } 39 | ``` 40 | 41 | ## Target System 42 | 43 | The target system of the HTTPS DID method is the web host that the domain name described by the DID resolves 44 | to when queried through the Domain Name System (DNS). 45 | 46 | ## DID Method Name 47 | 48 | The namestring that shall identify this DID method is: `https` 49 | 50 | A DID that uses this method MUST begin with the following prefix: `did:https`. Per the DID specification, this string 51 | MUST be in lowercase. The remainder of the DID, after the prefix, is specified below. 52 | 53 | ## Method Specific Identifier 54 | 55 | The method specific identifier is a fully qualified domain name that is secured by a TLS/SSL certificate. The formal 56 | rules describing valid domain name syntax are described in 57 | [RFC 1035](https://tools.ietf.org/html/rfc1035), [RFC 1123](https://tools.ietf.org/html/rfc1123), 58 | and [RFC 2181](https://tools.ietf.org/html/rfc2181). 59 | 60 | The method specific identifier must match the common name used in the SSL/TLS certificate, and it must not 61 | include IP addresses, port numbers, or directories behind the top-level domain extension. 62 | 63 | https-did = "did:https:" domain-name 64 | 65 | ### Example 66 | 67 | ``` 68 | did:https:w3c-ccg.github.io 69 | ``` 70 | 71 | ## JSON-LD Context Definition 72 | 73 | Note this DID method specification uses the `Secp256k1VerificationKey2018`, `Secp256k1SignatureAuthentication2018` 74 | types and an `ethereumAddress` instead of a `publicKeyHex`. 75 | 76 | The definition of the https DID JSON-LD context is: 77 | 78 | { 79 | "@context": 80 | { 81 | "ethereumAddress": "https://github.com/uport-project/ethr-did-resolver#ethereumAddress", 82 | "Secp256k1VerificationKey2018": "https://github.com/uport-project/ethr-did-resolver#Secp256k1VerificationKey2018", 83 | "Secp256k1SignatureAuthentication2018": "https://github.com/uport-project/ethr-did-resolver#Secp256k1VerificationKey2018", 84 | } 85 | } 86 | 87 | ## CRUD Operation Definitions 88 | 89 | ### Create (Register) 90 | 91 | Creating a DID is done by 92 | 1. applying at a domain name registrar for use of a domain name and 93 | 1. storing the location of a hosting service, the IP address at a DNS lookup service 94 | 1. creating the DID document JSON-LD file including a suitable keypair, e.g., using the 95 | Koblitz Curve, and storing the `did.json` file under the well-known URL. 96 | 97 | For the domain name `w3c-ccg.github.io`, the `did.json` will be available under the following URL: 98 | ``` 99 | did:https:w3c-ccg.github.io/.well-known/did.json 100 | ``` 101 | 102 | ### Read (Resolve) 103 | 104 | The following steps must be executed to resolve the DID document from an https DID: 105 | 106 | 1. Parse the fully qualified domain name from the identifier. 107 | 2. Generate an HTTPS URL to the expected location of the DID document by prepending `https://` and appending 108 | `/.well-known/did.json` to the domain name. 109 | 3. Perform an HTTP `GET` request to the URL using an agent that can successfully negotiate a secure HTTPS connection, 110 | which enforces the security requirements as described in [Security Considerations](Security-Considerations). 111 | 112 | ### Update 113 | 114 | To update the DID document, the `did.json`has to be updated. Please note that the DID will remain the same, but 115 | the contents of the DID document could change, e.g., by including a new verification key or adding service endpoints. 116 | 117 | ### Delete (Revoke) 118 | 119 | To delete the DID document, the `did.json`has to be removed or has to be no longer publicly available due to 120 | any other means. 121 | 122 | ## Security Considerations 123 | 124 | At least TLS 1.2 should be configured to use only strong ciphers suites and to use sufficiently large key sizes. 125 | As recommendations may be volatile these days, only the very latest recommendations should be used. However, 126 | as a rule of thumb, the following must be used: 127 | 128 | - Ephemeral keys are to be used. 129 | - ECDHE with one of the strong curves {X25519, brainpoolP384r1, NIST P-384, brainpoolP256r1, NIST P-256} shall be 130 | used as key exchange. 131 | - AESGCM or ChaCha20 with 256 bit large keys shall be used for bulk encryption 132 | - ECDSA with one of the strong curves {brainpoolP384r1, NIST P-384, brainpoolP256r1, NIST P-256} or RSA 133 | (at least 3072) shall be used. 134 | - Authenticated Encryption with Associated Data (AEAD) shall be used as Mac. 135 | - At least SHA256 shall be used, but SHA384 or POLY1305 are recommended. 136 | 137 | Examples of strong SSL/TLS configurations for now are: 138 | - `ECDHE-ECDSA-AES256-GCM-SHA384, TLSv1.2, Kx=ECDH, Au=ECDSA, Enc=AESGCM(256), Mac=AEAD` 139 | - `ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, Kx=ECDH, Au=RSA Enc=AESGCM(256), Mac=AEAD` 140 | - `ECDHE-ECDSA-CHACHA20-POLY1305, TLSv1.2, Kx=ECDH, Au=ECDSA, Enc=ChaCha20-Poly1305, Mac=AEAD` 141 | - `ECDHE-RSA-CHACHA20-POLY1305, TLSv1.2, Kx=ECDH, Au=RSA, Enc=ChaCha20-Poly1305, Mac=AEAD` 142 | - `ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, Kx=ECDH, Au=RSA, Enc=AESGCM(256), Mac=AEAD` 143 | - `ECDHE-ECDSA-AES256-GCM-SHA384, TLSv1.2, Kx=ECDH, Au=ECDSA, Enc=AESGCM(256), Mac=AEAD` 144 | 145 | It is recommended to adhere to [OWASP's](https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet) 146 | latest recommendations for hardening TLS configurations. 147 | 148 | Delete action can be performed by domain name registars or DNS lookup services. 149 | ## Reference Implementations 150 | 151 | The code at [https://github.com/uport-project/https-did-resolver](https://github.com/uport-project/https-did-resolver) 152 | is intended to present a reference implementation of this DID method. Any other implementations should ensure that 153 | they pass the test suite described in `/src/__tests__` before claiming compatibility. 154 | -------------------------------------------------------------------------------- /flows/generic.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Generic Request Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | 8 | ClientApp -> UportMobile : Signed Request (JWT) 9 | UportMobile -> Owner : Authorize Request? 10 | Owner -> UportMobile: Allow/Disallow 11 | UportMobile -> ClientApp: Response 12 | 13 | @enduml 14 | -------------------------------------------------------------------------------- /flows/generic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/generic.png -------------------------------------------------------------------------------- /flows/generic.svg: -------------------------------------------------------------------------------- 1 | Uport Generic Request FlowClientAppClientAppUportMobileUportMobileOwnerOwnerSigned Request (JWT)Authorize Request?Allow/DisallowResponse -------------------------------------------------------------------------------- /flows/identityverification.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Example Identity Verification Flow 4 | participant Issuer 5 | participant UportMobile 6 | actor Owner 7 | 8 | Issuer -> UportMobile : Selective Disclosure Request 9 | UportMobile -> Owner : Authorize Request? 10 | Owner -> UportMobile: Allow/Disallow 11 | UportMobile -> UportMobile: Sign Selective Disclosure Response 12 | UportMobile -> Issuer: Selective Disclosure Response 13 | Issuer <- Owner : Show passport 14 | Issuer -> UportMobile : Verification 15 | UportMobile -> Owner : Show Verification 16 | UportMobile -> Issuer : Acknowledgement 17 | 18 | @enduml 19 | -------------------------------------------------------------------------------- /flows/identityverification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/identityverification.png -------------------------------------------------------------------------------- /flows/identityverification.svg: -------------------------------------------------------------------------------- 1 | Example Identity Verification FlowIssuerIssuerUportMobileUportMobileOwnerOwnerSelective Disclosure RequestAuthorize Request?Allow/DisallowSign Selective Disclosure ResponseSelective Disclosure ResponseShow passportVerificationShow VerificationAcknowledgement -------------------------------------------------------------------------------- /flows/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Uport Request Flows" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/index.md" 6 | --- 7 | 8 | # Uport Request Flows 9 | 10 | A request will typically be signed by a client app and sent to mobile app using this generic request flow: 11 | 12 | ![Generic uPort Request Flow](generic.png) 13 | 14 | ## Unified Message Request Flow 15 | 16 | We are now introducing a new unified message request flow that will replace all other request flows. 17 | 18 | Each request now consists of a single [Signed Message](../messages/index.md) that is sent to the following endpoint: 19 | 20 | `https://id.uport.me/req/[JWT]` 21 | 22 | See each specific flow for specifics on each message type. 23 | 24 | ## Specific Application flows 25 | 26 | - [Selective Disclosure Flow](selectivedisclosure.md) 27 | - [Send Verification Claim Flow](verification.md) 28 | - [Request Verification Claim Flow](verificationreq.md) 29 | - [Ethereum Transaction Request Flow](tx.md) 30 | - [Private Chain Provisioning Flow](privatechain.md) 31 | 32 | ## Errors 33 | 34 | Wherever possible errors are based on [OAuth 2.0 RFC 6749 Authorization Errors](https://tools.ietf.org/html/rfc6749#section-4.1.2.1) 35 | 36 | An `error` parameter is returned as the response to the Client App, containing one of following: 37 | 38 | Error | Description 39 | ------------- | ----------- 40 | access_denied | User denies the request 41 | -------------------------------------------------------------------------------- /flows/personalsign.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Personal Signature Request Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/personalsign.md" 6 | --- 7 | 8 | # Personal Signature Request Flow 9 | 10 | Similar to a [Typed Data Signature Request](verificationreq.md), a client application can request that the user sign an arbitrary string of data. 11 | 12 | The following shows the basic flow: 13 | 14 | ![Personal Signature Request Flow](personalsign.png) 15 | 16 | ## Endpoint 17 | 18 | The request should be sent to the following URLs: 19 | 20 | - `https://id.uport.me/req/[JWT]` 21 | 22 | ## Send Request 23 | 24 | Create a valid signed [Personal Signature Request](../messages/personalsignreq.md). 25 | 26 | 27 | ## Client Callback 28 | 29 | The client app MAY include a URL where the response is returned from the user. This can be a HTTPS URL or a custom app URL which receives the response. 30 | 31 | Responses are param appended to a URL fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback URL instead. 32 | 33 | ### Successful Response 34 | 35 | param | Description 36 | -------------- | ----------- 37 | `personalSig` | [Personal Signature Response](../messages/personalsignresp.md) 38 | 39 | ### Errors 40 | 41 | An `error` parameter is returned as the response to the Client App, containing the following: 42 | 43 | Error | Description 44 | ------------- | ----------- 45 | `access_denied`| User denies the request 46 | -------------------------------------------------------------------------------- /flows/personalsign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/personalsign.png -------------------------------------------------------------------------------- /flows/personalsign.svg: -------------------------------------------------------------------------------- 1 | Uport Typed Data Signature Request FlowClientAppClientAppUportMobileUportMobileOwnerOwnerPersonal Signature RequestAuthorize Request?Allow/DisallowPersonal SignSignature + Orig. Request -------------------------------------------------------------------------------- /flows/privatechain.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Private Chain Provisioning Flows" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/privatechain.md" 6 | --- 7 | 8 | # Private Chain Provisioning Flow 9 | 10 | Experimental support for supporting Ethereum Accounts on private chains. 11 | 12 | The following shows the basic flow: 13 | 14 | ![Private Chain Provisioning Flow](privatechain.png) 15 | 16 | ## Requirements 17 | 18 | - Ethereum compatible blockchain 19 | - Public facing JSON RPC endpoint (RPC Gateway) 20 | 21 | ## Provisioning Methodology 22 | The provisioning of a new network happens at the selective disclosure request. In the request the JSON RPC endpoint is passed to the uPort Mobile app thru the `rpc` parameter. The uPort Mobile App stores the endpoint and then every tx request to that `networkId` is handled thru the specific endpoint. 23 | -------------------------------------------------------------------------------- /flows/privatechain.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Private Chain Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | participant RpcGateway 8 | database PrivateChain 9 | 10 | group Private Chain Discovery 11 | ClientApp -> UportMobile : Selective Disclosure Request\n Provide rpc endpoint 12 | UportMobile -> Owner : Authorize Request? 13 | Owner -> UportMobile: Allow/Disallow 14 | UportMobile -> UportMobile: Store netId -> rpc endpoint 15 | UportMobile -> UportMobile: Sign Selective Disclosure Response 16 | UportMobile -> ClientApp: Selective Disclosure Response 17 | end 18 | 19 | group Private Chain Interaction 20 | ClientApp -> UportMobile : Send Ethereum Transaction Request 21 | UportMobile -> Owner : Authorize Request? 22 | Owner -> UportMobile: Allow/Disallow 23 | UportMobile -> UportMobile: Sign Transaction 24 | UportMobile -> RpcGateway: Send transaction 25 | RpcGateway -> PrivateChain: Submit Transaction 26 | RpcGateway -> UportMobile: Signed Transaction 27 | UportMobile -> ClientApp: Transaction Hash 28 | ClientApp <-> PrivateChain: Transaction Confirmation 29 | end 30 | @enduml 31 | -------------------------------------------------------------------------------- /flows/privatechain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/privatechain.png -------------------------------------------------------------------------------- /flows/pushflow.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Desktop Web Server App to Uport Mobile Request Flow 4 | participant ClientApp 5 | participant MessagingServer 6 | participant PushServer 7 | participant UportMobile 8 | actor Owner 9 | 10 | group App Request Permission 11 | ClientApp -> ClientApp : Selective Disclosure Request requesting notifications permissions 12 | ClientApp -> Owner : Show QR code containing request 13 | Owner -> UportMobile: Scans QR code 14 | UportMobile -> Owner : Authorize Request? 15 | Owner -> UportMobile: Allow/Disallow 16 | UportMobile -> UportMobile: Sign Selective Disclosure Response with PushToken 17 | UportMobile -> MessagingServer: Selective Disclosure Response 18 | MessagingServer -> ClientApp: Selective Disclosure Response 19 | end 20 | 21 | group Send Request 22 | ClientApp -> PushServer : Send Request Authenticated with PushToken 23 | PushServer -> UportMobile : Notify App 24 | UportMobile -> Owner : Authorize Request? 25 | Owner -> UportMobile: Allow/Disallow 26 | UportMobile -> MessagingServer: Send response 27 | MessagingServer -> ClientApp: Receive Response 28 | end 29 | @enduml 30 | -------------------------------------------------------------------------------- /flows/pushflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/pushflow.png -------------------------------------------------------------------------------- /flows/selectivedisclosure.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Selective Disclosure Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/selectivedisclosure.md" 6 | --- 7 | 8 | # Selective Disclosure Flow 9 | 10 | A client application can request various information from the user. 11 | 12 | The following shows the basic flow: 13 | 14 | ![Selective Disclosure Flow](selectivedisclosure.png) 15 | 16 | ## Endpoint 17 | 18 | The request should be sent to the following URLs: 19 | 20 | - `https://id.uport.me/req/[JWT]` 21 | 22 | *The following endpoints are deprecated* 23 | 24 | - `me.uport:me` 25 | - `https://id.uport.me/me` 26 | 27 | ## Send Request 28 | 29 | Create a valid signed [Selective Disclosure Request](/messages/sharereq.md) and send it to the uPort mobile app. 30 | 31 | Signed example: 32 | 33 | `https://id.uport.me/req/eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 34 | 35 | The attributes `redirect_url` and `callback_type` can also be appended to the URL as encoded query parameters to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 36 | 37 | ### Deprecated Flows 38 | 39 | Create a valid signed or unsigned [Selective Disclosure Request](/messages/sharereq.md) and send it to the uPort mobile app. 40 | 41 | Signed example: 42 | 43 | `me.uport:me?requestToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 44 | 45 | The attributes `redirect_url` and `callback_type` can also be appended to the URL as encoded query parameters to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 46 | 47 | 48 | Unsigned example: 49 | 50 | `me.uport:me?callback_url=https://mysite.com/callback&label=My%20Site` 51 | 52 | ## Client Callback 53 | 54 | The client app MUST include a URL where the response is returned from the user. This can be a HTTPS URL or a custom app URL which receives the response. 55 | 56 | Responses are param appended to a URL fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback URL instead. 57 | 58 | ### Successful Response 59 | 60 | param | Description 61 | -------------- | ----------- 62 | `access_token` | [Selective Disclosure Response](/messages/shareresp.md) 63 | 64 | ### Errors 65 | 66 | An `error` parameter is returned as the response to the Client App, containing one of following: 67 | 68 | Error | Description 69 | ------------- | ----------- 70 | access_denied | User denies the request 71 | -------------------------------------------------------------------------------- /flows/selectivedisclosure.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Selective Disclosure Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | 8 | ClientApp -> UportMobile : Selective Disclosure Request 9 | UportMobile -> Owner : Authorize Request? 10 | Owner -> UportMobile: Allow/Disallow 11 | UportMobile -> UportMobile: Sign Selective Disclosure Response 12 | UportMobile -> ClientApp: Selective Disclosure Response 13 | 14 | @enduml 15 | -------------------------------------------------------------------------------- /flows/selectivedisclosure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/selectivedisclosure.png -------------------------------------------------------------------------------- /flows/selectivedisclosure.svg: -------------------------------------------------------------------------------- 1 | Uport Selective Disclosure FlowClientAppClientAppUportMobileUportMobileOwnerOwnerSelective Disclosure RequestAuthorize Request?Allow/DisallowSign Selective Disclosure ResponseSelective Disclosure Response -------------------------------------------------------------------------------- /flows/tx.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ethereum Transaction Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/tx.md" 6 | --- 7 | 8 | # Ethereum Transaction Flow 9 | 10 | A client application can request that a user signs an Ethereum transaction. 11 | 12 | The following shows the basic flow: 13 | 14 | ![Ethereum Transaction Flow](tx.png) 15 | 16 | ## Endpoint 17 | 18 | Signed request should be sent to the following URLs: 19 | 20 | - `https://id.uport.me/req/[JWT]` 21 | 22 | *The following endpoints are deprecated* 23 | 24 | The request should be sent to one of the following URLs: 25 | 26 | - `me.uport:[ADDRESS]` 27 | - `ethereum:[ADDRESS]` 28 | - `https://id.uport.me/[ADDRESS]` 29 | 30 | ## Send Request 31 | 32 | Create a valid signed or unsigned [Ethereum Transaction Request](../messages/tx.md) and send it to the uPort mobile app. 33 | 34 | Signed example: 35 | 36 | `https://id.uport.me/req/eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 37 | 38 | The attributes `redirect_url` and `callback_type` can also be appended to the URL as encoded query parameters to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 39 | 40 | Unsigned example: (Deprecated) 41 | 42 | `me.uport:2oDZvNUgn77w2BKTkd9qKpMeUo8EL94QL5V?transfer(address%200xdeadbeef%2C%20uint%205)&callback_url=https://mysite.com/callback&label=My%20Site` 43 | 44 | ## Client Callback 45 | 46 | The client app SHOULD include a URL where the response is returned from the user. This can be a https url or a custom app url which receives the response. 47 | 48 | Responses are param appended to a url fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback url instead. 49 | 50 | ### Successful Response 51 | 52 | param | Description 53 | ----- | ----------- 54 | `tx` | Ethereum Transaction Hash 55 | 56 | The client app SHOULD verify that the transaction has been successfully included in a block. 57 | 58 | ### Errors 59 | 60 | An `error` parameter is returned as the response to the Client App, containing one of following: 61 | 62 | Error | Description 63 | ------------- | ----------- 64 | access_denied | User denies the request 65 | processing_error | Something went wrong submitting transaction to the blockchain 66 | -------------------------------------------------------------------------------- /flows/tx.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Ethereum Transaction Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | participant FuelingService 8 | database Ethereum 9 | 10 | ClientApp -> UportMobile : Ethereum Transaction Request 11 | UportMobile -> Owner : Authorize Request? 12 | Owner -> UportMobile: Allow/Disallow 13 | UportMobile -> UportMobile: Sign Transaction 14 | UportMobile -> FuelingService: Request gas for transaction 15 | FuelingService -> Ethereum: Fueling Transaction 16 | UportMobile -> Ethereum: Signed Transaction 17 | UportMobile -> ClientApp: Transaction Hash 18 | ClientApp <-> Ethereum: Transaction Confirmation 19 | 20 | @enduml 21 | -------------------------------------------------------------------------------- /flows/tx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/tx.png -------------------------------------------------------------------------------- /flows/tx.svg: -------------------------------------------------------------------------------- 1 | Uport Ethereum Transaction FlowClientAppClientAppUportMobileUportMobileOwnerOwnerFuelingServiceFuelingServiceEthereumEthereumEthereum Transaction RequestAuthorize Request?Allow/DisallowSign TransactionRequest gas for transactionFueling TransactionSigned TransactionTransaction HashTransaction Confirmation -------------------------------------------------------------------------------- /flows/typeddatasig.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tyed Data Signature Request Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/typeddatasig.md" 6 | --- 7 | 8 | 9 | # Typed Data Signature Request Flow 10 | 11 | Similar to a [Verification request](verificationreq.md), a client application can request that the user sign a piece of typed data, for example one conforming to the EIP712 Specification. 12 | 13 | The following shows the basic flow: 14 | 15 | ![Typed Data Signature Request Flow](typeddatasig.png) 16 | 17 | ## Endpoint 18 | 19 | The request should be sent to the following URLs: 20 | 21 | - `https://id.uport.me/req/[JWT]` 22 | 23 | ## Send Request 24 | 25 | Create a valid signed [Typed Data Signature Request](../messages/signtypeddatareq.md). 26 | 27 | 28 | ## Client Callback 29 | 30 | The client app MAY include a URL where the response is returned from the user. This can be a HTTPS URL or a custom app URL which receives the response. 31 | 32 | Responses are param appended to a URL fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback URL instead. 33 | 34 | ### Successful Response 35 | 36 | param | Description 37 | -------------- | ----------- 38 | `typedDataSig` | [Signed Typed Data Response](../messages/signtypeddataresp.md) 39 | 40 | ### Errors 41 | 42 | An `error` parameter is returned as the response to the Client App, containing the following: 43 | 44 | Error | Description 45 | ------------- | ----------- 46 | access_denied | User denies the request 47 | -------------------------------------------------------------------------------- /flows/typeddatasig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/typeddatasig.png -------------------------------------------------------------------------------- /flows/typeddatasig.svg: -------------------------------------------------------------------------------- 1 | Uport Typed Data Signature Request FlowClientAppClientAppUportMobileUportMobileOwnerOwnerTyped Data Signature RequestAuthorize Request?Allow/DisallowSign Typed DataSignature + Orig. Request -------------------------------------------------------------------------------- /flows/verification.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Send Verification Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/verification.md" 6 | --- 7 | 8 | # Send Verification Flow 9 | 10 | A client application can verify information about a user 11 | 12 | The following shows the basic flow: 13 | 14 | ![Verified Claim Flow](verification.png) 15 | 16 | A more complete example of using this with a simple Identify Verification service can be seen here. In this case the user starts with a [Selective Disclosure Flow](selectivedisclosure.md) to an issuer. An offline process could be in place where the user now shows their passport to the Issuer after which they will receive an identity verification in their mobile app. 17 | 18 | ![Example Identity Verified Claim Flow](identityverification.png) 19 | 20 | ## Endpoint 21 | 22 | The request should be sent to the following URLs: 23 | 24 | - `https://id.uport.me/req/[JWT]` 25 | 26 | *The following endpoints are deprecated* 27 | 28 | - `me.uport:add` 29 | - `https://id.uport.me/add` 30 | 31 | ## Send Verifications 32 | 33 | Create a [Verified Claims](../messages/verification.md) and attach it to the `https://id.uport.me/req/` url. Present this URL to the user and the app will open. 34 | 35 | Example: 36 | 37 | `https://id.uport.me/req/eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 38 | 39 | ### Deprecated Flows 40 | 41 | A deprecated version adds it to the uPort mobile app in the `attestations` query parameter. 42 | 43 | Example: 44 | 45 | `me.uport:add?attestations=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 46 | 47 | The attributes `redirect_url` and `callback_type` can also be appended to the URL as encoded query parameters to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 48 | 49 | 50 | ## Client Callback 51 | 52 | The client app MAY include a URL where the response is returned from the user. This can be a HTTPS URL or a custom app URL which receives the response. The callback is strictly optional and is mainly used if you need to acknowledge in your UX that the attestation has been received. 53 | 54 | ### Successful Response 55 | 56 | param | Description 57 | ------ | ----------- 58 | status | always `ok` 59 | -------------------------------------------------------------------------------- /flows/verification.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Send Verified Claim Flow 4 | participant Issuer 5 | participant UportMobile 6 | actor Owner 7 | 8 | Issuer <-> Owner : On or Off-line Identity verification 9 | Issuer -> UportMobile : Verified Claim 10 | UportMobile -> Owner : Show Verification 11 | UportMobile -> Issuer : Acknowledgement 12 | 13 | @enduml 14 | -------------------------------------------------------------------------------- /flows/verification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/verification.png -------------------------------------------------------------------------------- /flows/verification.svg: -------------------------------------------------------------------------------- 1 | Uport Send Verified Claim FlowIssuerIssuerUportMobileUportMobileOwnerOwnerOn or Off-line Identity verificationVerified ClaimShow VerificationAcknowledgement -------------------------------------------------------------------------------- /flows/verificationreq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Verification Request Flow" 3 | category: "flows" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/flows/verificationreq.md" 6 | --- 7 | 8 | 9 | # Verified Claim Request Flow 10 | 11 | A client application can request that the user sign a Verified Claim. 12 | 13 | The following shows the basic flow: 14 | 15 | ![Verified Request Flow](verificationreq.png) 16 | 17 | ## Endpoint 18 | 19 | The request should be sent to the following URLs: 20 | 21 | - `https://id.uport.me/req/[JWT]` 22 | 23 | ## Send Request 24 | 25 | Create a valid signed [Verified Claim Request](../messages/verificationreq.md) and send it to the uPort mobile app. 26 | 27 | Signed example: 28 | 29 | `https://id.uport.me/req/eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp...` 30 | 31 | 32 | ## Client Callback 33 | 34 | The client app MUST include a URL where the response is returned from the user. This can be a HTTPS URL or a custom app URL which receives the response. 35 | 36 | Responses are param appended to a URL fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback URL instead. 37 | 38 | ### Successful Response 39 | 40 | param | Description 41 | -------------- | ----------- 42 | `verification` | [Verified Claim](../messages/verification.md) 43 | 44 | ### Errors 45 | 46 | An `error` parameter is returned as the response to the Client App, containing one of following: 47 | 48 | Error | Description 49 | ------------- | ----------- 50 | access_denied | User denies the request 51 | -------------------------------------------------------------------------------- /flows/verificationreq.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Verified Claim Request Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | 8 | ClientApp -> UportMobile : Verified Claim Request 9 | UportMobile -> Owner : Authorize Request? 10 | Owner -> UportMobile: Allow/Disallow 11 | UportMobile -> UportMobile: Sign Verified Claim 12 | UportMobile -> ClientApp: Send Verified Claim 13 | 14 | @enduml 15 | -------------------------------------------------------------------------------- /flows/verificationreq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/flows/verificationreq.png -------------------------------------------------------------------------------- /flows/verificationreq.svg: -------------------------------------------------------------------------------- 1 | Uport Verified Claim Request FlowClientAppClientAppUportMobileUportMobileOwnerOwnerVerified Claim RequestAuthorize Request?Allow/DisallowSign Verified ClaimSend Verified Claim -------------------------------------------------------------------------------- /messages/claims.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Issuer Claims" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/claims.md" 6 | --- 7 | 8 | # Issuer Claims 9 | 10 | The issuer of a message is the signer of the request. The issuer can present claims about themselves. 11 | 12 | This can be used over time to build trust between the issuer of the message and the consuming party. 13 | 14 | ## Verified Claims about Issuer 15 | 16 | A selection of [verified claims](/messages/verification.md) about the issuer can be included in the `vc` field of a message. 17 | 18 | The verified claims can either be included in their entirety as [JWT's](https://tools.ietf.org/html/rfc7519)'s in an array: 19 | 20 | ```js 21 | { 22 | "iss":"did:ethr:0x012abcd...", 23 | "vc": ["Verified Claim JWT 1", "Verified Claim JWT 2"] 24 | } 25 | ``` 26 | 27 | Or as an array of IPFS hashes containing individual JWTs. 28 | 29 | ```js 30 | { 31 | "iss":"did:ethr:0x012abcd...", 32 | "vc": ["/ipfs/QmbpLchVBiF5Deg8Dp31y4AUSNLiBbF92mHAp3xDcAfg4k"] 33 | } 34 | ``` 35 | 36 | 37 | **About image links:** Images must be stored in IPFS. This is to avoid malicious tracking of users through images. 38 | 39 | Upload the image like this: 40 | 41 | ```bash 42 | > curl "https://ipfs.infura.io:5001/api/v0/add?pin=true" -F file=@logo.png 43 | {"Name":"logo.png","Hash":"QmV3pEPwSzkQVMPmkvpWRvWRxexdMrCNayMnZeao8dibm4","Size":"5779"} 44 | ``` 45 | 46 | Remember to prefix the hash with `/ipfs/`. 47 | 48 | ### IPLD Format 49 | 50 | To reduce the size of messages we remember storing the claims as an as an [IPLD Document](https://github.com/ipld/specs/blob/master/IPLD.md). 51 | QmbpLchVBiF5Deg8Dp31y4AUSNLiBbF92mHAp3xDcAfg4k 52 | 53 | Upload your claims document to IPFS. 54 | 55 | Example document: 56 | 57 | ```js 58 | { 59 | "name":"My dApp", 60 | "description":"A really cool place to do stuff decentralized", 61 | "url":"https://mydapp.example", 62 | "profileImage":{"/":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"}, 63 | "bannerImage":{"/":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"}, 64 | } 65 | ``` 66 | 67 | Save the above file to `claims.json` 68 | 69 | ```bash 70 | > curl "https://ipfs.infura.io:5001/api/v0/add?pin=true" -F file=@claims.json 71 | {"Name":"claims.json","Hash":"QmbpLchVBiF5Deg8Dp31y4AUSNLiBbF92mHAp3xDcAfg4k","Size":"291"} 72 | ``` 73 | 74 | Now use the returned hash in your requests: 75 | 76 | ```js 77 | { 78 | "iss":"did:ethr:0x012abcd...", 79 | "vc": ["/ipfs/QmbpLchVBiF5Deg8Dp31y4AUSNLiBbF92mHAp3xDcAfg4k"] 80 | } 81 | ``` 82 | 83 | **Note:** For links to other IPFS files such as images, you must add then as an [IPLD merkle link](https://github.com/ipld/specs/blob/master/IPLD.md#what-is-a-merkle-link). This just means that instead of using the string `ipfs/QmV3pEPwSzkQVMPmkvpWRvWRxexdMrCNayMnZeao8dibm4` as in an embedded document. You use the object `{"/":"/ipfs/QmV3pEPwSzkQVMPmkvpWRvWRxexdMrCNayMnZeao8dibm4"}` instead. 84 | 85 | ### Parameters 86 | 87 | Name | Description | Required 88 | ---- | ----------- | -------- 89 | `name`| Name of Issuer | no 90 | `description`| Single sentence describing the Issuer | no 91 | `url` | URL of dApp/Requester | no 92 | `profileImage` | Avatar or logo as JPEG or PNG of requester on IPFS as a `/ipfs/[HASH]` in embedded claims or a [merkle link](https://github.com/ipld/specs/blob/master/IPLD.md#what-is-a-merkle-link) for IPLD documents| no 93 | `bannerImage` | Banner to e used on certain request cards as JPEG or PNG of requester on IPFS as a `/ipfs/[HASH]` in embedded claims or a [merkle link](https://github.com/ipld/specs/blob/master/IPLD.md#what-is-a-merkle-link) for IPLD documents| no 94 | 95 | -------------------------------------------------------------------------------- /messages/encryption.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Message Encryption" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/encryption.md" 6 | --- 7 | 8 | # Message Encryption 9 | 10 | Some [message transports](/transports/index.md) are not directly secure and require encryption of the message. We currently use the [box public key auhtenticated encryption algorithm](http://nacl.cr.yp.to/box.html) and thus both parties need a Curve25519 public key to be able to create a secure session. 11 | 12 | ## Encryption Public Key resolution 13 | 14 | A public key can either be published as part of the [DID document](/pki/diddocument.md). In this case it looks for a `publicKey` of type `Curve25519EncryptionPublicKey` in the DID document. eg. 15 | 16 | ```js 17 | { 18 | '@context': 'https://w3id.org/did/v1', 19 | 'id': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX', 20 | 'publicKey': [ 21 | ..., 22 | { 23 | 'id': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-2', 24 | 'type': 'Curve25519EncryptionPublicKey', 25 | 'owner': 'did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX', 26 | 'publicKeyBase64': 'QCFPBLm5pwmuTOu+haxv0+Vpmr6Rrz/DEEvbcjktQnQ=' 27 | }], 28 | ... 29 | } 30 | ``` 31 | 32 | Currently the public key must be base64 encoded in the `publicKeyBase64` attribute. 33 | 34 | ## Request/Response resolution 35 | 36 | If your DID method does not include an encryption key it can also be included as part of the initiation of a session setup between requesting and disclosing parties as part of the [Selective Disclosure Flow](/flows/selectivedisclosure.md). 37 | 38 | If the requesting party does not include the encryption public key in the DID document they can include a `boxPub` attribute as part of the [Selective Disclosure Request](/messages/sharereq.md). 39 | 40 | If the disclosing party does not include the encryption public key in the DID document they can also include a `boxPub` attribute as part of the [Selective Disclosure Response](/messages/shareresp.md). 41 | 42 | ### JOSE JWK Standards 43 | 44 | We currently do not support the JOSE JWT standards including the [JOSE CFRG ECDH RFC](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-06). This is primarily for reasons of compactness. Our responses have to be limited in size to be able to comfortably fit in a QR code. 45 | 46 | We plan to support these in the future. 47 | 48 | ## Encrypting the request 49 | 50 | We use the [ERC 1098](https://github.com/ethereum/EIPs/pull/1098) encryption method which uses an ephemeral sending key and the [box](https://github.com/dchest/tweetnacl-js/blob/master/README.md#public-key-authenticated-encryption-box) method from [tweet-nacl](https://tweetnacl.js.org/). This allows the recipient to decrypt a message without having to resolve the public key of the sender. 51 | 52 | The following method is used: 53 | 54 | 1. Create the signed [JWT payload like normal](/messages/index.md) 55 | 2. JWT is padded with `\0`s to the nearest multiple of 64 bytes (see "padding" below) 56 | 3. Create an ephemeral keypair using [`nacl.box.keyPair()`](https://github.com/dchest/tweetnacl-js/blob/master/README.md#naclboxkeypair) 57 | 4. Create a random 24 bytes `nonce` using [`nacl.randomBytes(nacl.box.nonceLength)`](https://github.com/dchest/tweetnacl-js/blob/master/README.md#naclrandombyteslength) 58 | 5. encrypt the resulting JWT using the [`nacl.box(message, nonce, recipient publicKey, ephemeralKeyPair.secretKey)`](https://github.com/dchest/tweetnacl-js/blob/master/README.md#naclboxmessage-nonce-theirpublickey-mysecretkey) 59 | 6. Combine the base64 encoded versions of the above `nonce`, `ephemPublicKey` and `ciphertext` values together with the `version` of `x25519-xsalsa20-poly1305` in a JSON payload. 60 | 61 | ```js 62 | { version: 'x25519-xsalsa20-poly1305', 63 | nonce: '1dvWO7uOnBnO7iNDJ9kO9pTasLuKNlej', 64 | ephemPublicKey: 'FBH1/pAEHOOW14Lu3FWkgV3qOEcuL78Zy+qW1RwzMXQ=', 65 | ciphertext: 'f8kBcl/NCyf3sybfbwAKk/np2Bzt9lRVkZejr6uh5FgnNlH/ic62DZzy' } 66 | ``` 67 | 68 | ## Decrypting the request 69 | 70 | You need to know the recipients secretKey. 71 | 72 | 1. Check that the `version` field is `x25519-xsalsa20-poly1305` 73 | 2. Decode the base64 encoded `nonce`, `ephemPublicKey` and `ciphertext` attributes 74 | 3. Decrypt it message using [`nacl.box.open(ciphertext, nonce, ephemPublicKey, recieverEncryptionPrivateKey)`](https://github.com/dchest/tweetnacl-js/blob/master/README.md#naclboxopenbox-nonce-theirpublickey-mysecretkey) 75 | 4. Strip any trailing `\0` from the payload 76 | 5. Decode JWT as normal 77 | 78 | 79 | ## Padding 80 | 81 | To avoid leaking information about the specific size of the payload, we need to pad the payload to the nearest multiple of 64 bytes. The padding should be done with `\0` bytes. 82 | 83 | After decrypting any trailing `\0`s are removed before passing the result to the JWT verifier. 84 | 85 | ## Legacy Encryption of the request 86 | 87 | This method is used in early versions of uPort and is only documented here for historical reasons. 88 | 89 | To encrypt the request [NACL Box Public Key Encryption](http://nacl.cr.yp.to/box.html) is used. An ephemeral key is generated in order to encrypt the data to the public key of the user. 90 | 91 | The `encryptionPublicKey` is encoded as a Base64 string. The decoded public key `upk` should be 32 bytes. 92 | The ephemeral key pair is generated using the NACL library. Both secret key `eSK` and the public key `epk` has to be 32 bytes. 93 | The nonce `n` should be randomly generated and of length 24. 94 | 95 | Using the data above a NACL Box can be used to encrypt the message: `c = crypto_box(m, n, upk, eSK)` 96 | 97 | ### Encoding the encrypted data 98 | 99 | In order for the mobile app to be able to decrypt the ciphertext it also needs `epk` and `n`. This needs to be formatted in a specific way. Most importantly the parameters need to be encoded as Base64 strings. 100 | 101 | Simply create a JSON object and encode it as a string: `{"from":"","nonce":"","ciphertext":""}`. This string is now our `encrypted message`. 102 | -------------------------------------------------------------------------------- /messages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Off-chain Messages" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/index.md" 6 | --- 7 | 8 | # Off-chain Messages 9 | 10 | Most request and responses are performed privately off-chain between the different parties to a flow. 11 | 12 | ## JSON Web Token 13 | 14 | Most off-chain messages consist of signed JWTs (JSON Web Tokens) as defined in [RFC 7519](https://tools.ietf.org/html/rfc7519). 15 | 16 | ### Requirements 17 | 18 | We currently only support signatures using the [secp256k1 ECDSA curve](https://en.bitcoin.it/wiki/Secp256k1), which is also used by both Bitcoin and Ethereum. 19 | 20 | #### JOSE Header 21 | 22 | The [JOSE header](https://tools.ietf.org/html/rfc7519#section-5) indicates the signing algorithm used in the JWT. This MUST contain the following: 23 | 24 | ```json 25 | {"typ": "JWT", "alg": "ES256K"} 26 | ``` 27 | 28 | #### Attributes 29 | 30 | The JWT spec calls these claims, but we use the term "claims" for identity-specific data. So in this document we will call these standard JWT "claims" "attributes" instead. 31 | 32 | Name | Description | Required 33 | ---- | ----------- | -------- 34 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the signing identity| yes 35 | [`sub`](https://tools.ietf.org/html/rfc7519#section-4.1.2) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the subject of the JWT| no 36 | [`aud`](https://tools.ietf.org/html/rfc7519#section-4.1.3) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) or URL of the audience of the JWT. Our libraries or app will not accept any JWT that has someone else as the audience| no 37 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 38 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 39 | 40 | Non Standard attributes: 41 | 42 | Name | Description | Required 43 | ---- | ----------- | -------- 44 | `callback` | Callback URL for returning the response to a request | no 45 | `type` | Type of Message | no 46 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 47 | `boxPub` | 32 byte base64 encoded [`Curve25519`](http://nacl.cr.yp.to/box.html) public key | no 48 | 49 | The following attributes can also be appended to the signed request as URL encoded query parameters outside of the signed payload. 50 | 51 | Name | Description | Required 52 | ---- | ----------- | -------- 53 | `redirect_url` | The URL that control is returned to once a request is complete (mobile). Base url/domain must match callback in JWT. | no 54 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a HTTP POST or open the link (`redirect`). If unspecified, the mobile app will attempt to pick the correct one| no 55 | 56 | These options allow you to tell the client how you want to receive the response. If a redirect_url is provided, the client will post the response to the callback in the JWT and then call the given redirect_url to return control to the original callee. You can also add any contextual data in the redirect_url query params or as a url fragment (i.e. you may want to map requests to responses). If a redirect_url is provided and a callback_type redirect is provided the response will be passed as url fragment with the redirect_url. If no redirect_url is provided, it will use the callback in JWT and will use the callback_type if provided or the client will attempt to choose the correct type. 57 | 58 | ### Signature Verification 59 | 60 | Each uPort compatible JWT must be signed using an secp256k1 curve. The public key is resolved for the `iss` using the [uPort PKI](/pki/index.md). 61 | 62 | ### Message Encryption 63 | 64 | Some [message transports](/transports/index.md) are not secure and require messages to be encrypted. See [our encryption specs](/messages/encryption.md) for more detail. 65 | 66 | ## Unsigned Requests (Deprecated) 67 | 68 | Many apps that run 100% in the browser do not have a secure way of signing a request. Therefore we provide unsigned versions of certain requests. 69 | 70 | ### Standard Unsigned Parameters 71 | 72 | There are certain standardized parameters that are provided using HTTP query params in the request. Some of these are based on parameters in the [OAuth 2.0 RFC 6749 Spec](https://tools.ietf.org/html/rfc6749): 73 | 74 | Name | Description | Required 75 | ---- | ----------- | -------- 76 | `client_id` | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the requesting identity | no 77 | `callback_url` | The URL that receives the response | no 78 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a HTTP POST or open the link (`redirect`). If unspecified the mobile app will attempt to pick the correct one| no 79 | `label` | Plain text name of client to be displayed to user | no 80 | 81 | ## Message types 82 | 83 | There are several standard message types that the uPort mobile app knows how to handle or create: 84 | 85 | - **[Selective Disclosure Request](sharereq.md)** for asking private data from a user 86 | - **[Selective Disclosure Response](shareresp.md)** signed by the app as a response to a Selective Disclosure Request 87 | - **[Verified Claim](verification.md)** - signed claim by one party about another party 88 | - **[Verified Claim Request](verificationreq.md)** - Request a signed claim by one party about another party 89 | - **[Ethereum Transaction Request](tx.md)** for requesting a user to sign a transaction 90 | - **[Private Chain Provisioning Message](privatechain.md)** for provisioning an identity on a private Ethereum chain 91 | -------------------------------------------------------------------------------- /messages/personalsignreq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Personal Sign Request" 3 | category: "messages" 4 | "type": "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/personalsignreq.md" 6 | --- 7 | 8 | # Personal Sign Request 9 | 10 | The Personal Sign Request is a message created by a client app and sent to a user's mobile app, which contains a string of arbitrary, unstructured data to be signed. This is an adaptation of the `personal_sign` RPC call to a uPort-based workflow. 11 | 12 | #### Attributes 13 | 14 | The JWT shares these attributes with the [Share Request](sharereq.md) and [Verification Request](verificationreq.md): `iat`, `exp`, `callback`, and `vc`; it also shares the `riss` field with [Verification Request](verificationreq.md). The data in the `data` field should be a string containing the hex characters that make up the UTF-8 representation of the data to be signed, prefixed with `0x`. 15 | 16 | The following additional attributes of the JWT are supported: 17 | 18 | Name | Description | Required 19 | ---- | ----------- | -------- 20 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 21 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 22 | `type` | MUST have the value `personalSignReq` | yes 23 | `data` | A string containing the hex encoding of the data to be signed | yes 24 | `callback` | Callback URL for returning the response to a request (may be deprecated in future) | no 25 | `riss` | The DID of the identity you want to sign the Verified Claim | no 26 | `from` | Hex encoded address requested to sign the transaction. If not specified the user will select an account. | no 27 | `net` | network id of Ethereum chain of identity eg. `0x4` for `rinkeby`. It defaults to `0x1` for `mainnet`. | no 28 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 29 | 30 | 31 | Example Personal Sign request: 32 | 33 | ```json 34 | { 35 | "riss":"did:ethr:IDENTITY_THAT_WILL_SIGN_THE_CLAIM", 36 | "type": "personalSignReq", 37 | "data": "0xdeaddeadbeefbeef", 38 | "callback": "https://example.com", 39 | "exp": 123456789 40 | } 41 | ``` -------------------------------------------------------------------------------- /messages/personalsignresp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sign Typed Data Response" 3 | category: "messages" 4 | "type": "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/signtypeddataresp.md" 6 | --- 7 | 8 | # Personal Sign Response 9 | 10 | A personal sign response is a JWT containing within it a hex string of data, and an object representing the signature of the hash of that data. See also: [Personal Sign Request](personalsignreq.md) 11 | 12 | # Attributes 13 | 14 | The JWT contains three required fields, `iat`, `iss`, `request`, and `signature`. The `request` is an EIP712 signature request, as definied in the [EIP712 specification for typed data](https://eips.ethereum.org/EIPS/eip-712). `iss` and `iat` are defined as the issuer of the JWT, and the time at which it was issued, as usual. The `signature` field is an object containing the components `r`, `s`, and `v` which make up the signature over the typed data specified in the `request` field. The JWT may optionally also contain the `exp`, `vc` fields. 15 | 16 | Name | Description | Required 17 | ---- | ----------- | -------- 18 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 19 | `type` | MUST have the value `personalSignResp` | yes 20 | `iss` | The DID of the issuer of the JWT, not necessarily the same as the signer in `signature` | yes 21 | `data` | A hex string representing the data that was signed. | yes 22 | `signature` | An object containing `r`, `s`, and `v`, the components of the signature | yes 23 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 24 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 25 | 26 | 27 | 28 | An example personal signature response: 29 | ```json 30 | { 31 | "iat": 123456789, 32 | "type": "personalSignResp", 33 | "iss": "did:ethr:0x...", 34 | "data": "0xdeaddeadbeefbeef", 35 | "signature": { 36 | "r": "0x...", 37 | "s": "0x...", 38 | "v": "0x..." 39 | }, 40 | "exp": 123456789, 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /messages/sharereq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Selective Disclosure Request" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/sharereq.md" 6 | --- 7 | 8 | # Selective Disclosure Request 9 | 10 | The Selective Disclosure Request is created by a client app and sent to a user's mobile app during the [Selective Disclosure Flow](/flows/selectivedisclosure.md). 11 | 12 | The request can be either signed or unsigned. 13 | 14 | #### Attributes 15 | 16 | The following attributes of the JWT are supported: 17 | 18 | Name | Description | Required 19 | ---- | ----------- | -------- 20 | `type` | MUST have the value `shareReq` | yes 21 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the signing identity| yes 22 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 23 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 24 | `callback` | Callback URL for returning the response to a request | no 25 | `net` | network id of Ethereum chain of identity eg. `0x4` for `rinkeby` | no 26 | `act` | Ethereum account type: `general` users choice (default), `segregated` a unique smart contract based account will be created for requesting app, `keypair` a unique keypair based account will be created for requesting app, `devicekey` request a new device key for a [Private Chain Account](../flows/privatechain.md), `none` no account is returned | no 27 | `requested` | DEPRECATED The self signed claims requested from a user. Array of claim types for self signed claims. Currently supported: `["name", "email", "image", "country", "phone"]` | no 28 | `verified` | DEPRECATED The verified claims requested from a user. Array of claim types for self signed claims eg: `["name", "email"]`, see [Verified Claims](/messages/verification.md) | no 29 | `claims` | Requirements for claims requested from a user. See [Claims Specs](#claims-spec) and [Verified Claims](/messages/verification.md) | no 30 | `permissions` | An array of permissions requested. Currently only supported is `notifications` | no 31 | `boxPub` | 32 byte base64 encoded [`Curve25519`](http://nacl.cr.yp.to/box.html) public key of requesting identity. Use to encrypt messages sent to callback URL| no 32 | `issc` | The self signed claims for the `iss` of this message. Either as an Object of claim types for self signed claims eg: `{"name":"Some Corp Inc", "url":"https://somecorp.example","image":{"/":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"}}` or the IPFS Hash of a JSON encoded equivalent. See [Issuer Claims](/messages/claims.md) | no 33 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 34 | `rpc` | URL for the JSON RPC endpoint, ie: `https://mainnet.infura.io/`. Useful for [Private Networks](/flows/privatechain.md). The `net_version` method shoud return the same networkId as the filed `net`. The JSON RPC endpoint must provide the following methods: `eth_getTransactionCount`, `eth_getBalance`, `eth_estimateGas`, `eth_gasPrice`, `eth_sendRawTransaction`, `eth_getTransactionReceipt` | no 35 | 36 | The attributes `redirect_url` and `callback_type` can also be appended to the signed request as URL encoded query parameters outside of the signed payload. They are used to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 37 | 38 | ### Claims Spec 39 | 40 | A Claims Spec allows you to request claims with very specific properties. This is based on the `claims` spec in [OpenID-Connect](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter) but adapted to support Verifiable Claims. 41 | 42 | This replaces the `requested` and `verified` parameters of this request. You may still include `requested` and `verified` to provide support for older clients. But they will be ignored if by newer clients if the `claims` field is present. 43 | 44 | ```js 45 | { 46 | iss: 'did:web:somesite.com', 47 | type: 'shareReq', 48 | claims: { 49 | verifiable: { 50 | email: { 51 | iss: [ 52 | { 53 | did: 'did:web:uport.claims', 54 | url: 'https://uport.claims/email' 55 | }, 56 | { 57 | did: 'did:web:sobol.io', 58 | url: 'https://sobol.io/verify' 59 | } 60 | ], 61 | reason: 'Whe need to be able to email you' 62 | }, 63 | nationalIdentity: { 64 | essential: true, 65 | iss: [ 66 | { 67 | did: 'did:web:idverifier.claims', 68 | url: 'https://idverifier.example' 69 | } 70 | ], 71 | reason: 'To legally be able to open your account' 72 | } 73 | }, 74 | user_info: { 75 | name: { essential: true, reason: "Show your name to other users"}, 76 | country: null 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | #### Verifiable Claims Spec 83 | 84 | This is an json object that lives at the `verifiable` key within the `claims` object. It is not required, but provides you flexibility in clearly specifying the kind of claims you need. 85 | 86 | Name | Description | Required 87 | ---- | ----------- | -------- 88 | type | The key in the `verifiable` object is the claims type requested | yes 89 | essential | This claim is essential. A response should not be returned if user does not have this claim | no 90 | iss | Array of `{did, url}` objects where `did` is DID of allowed issuer of claims and `url` is URL for obtaining claim, `did` is required but `url` is not | no 91 | reason | Short string explaining why you need this | no 92 | 93 | Examples: 94 | 95 | ```js 96 | { 97 | email: { 98 | essential: true, 99 | iss: [ 100 | { 101 | did: 'did:web:uport.claims', 102 | url: 'https://uport.claims/email' 103 | }, 104 | { 105 | did: 'did:web:sobol.io', 106 | url: 'https://sobol.io/verify' 107 | } 108 | ], 109 | reason: 'Whe need to be able to email you' 110 | } 111 | } 112 | ``` 113 | 114 | #### User Info Spec 115 | 116 | This is an json object that lives at the `user_info` key within the `claims` object. It is not required, but provides you flexibility in clearly specifying the kind of self presented user claims you need. 117 | 118 | NOTE: These are claims specifically made by the user themselves and are not verifiable by an external party. 119 | 120 | Name | Description | Required 121 | ---- | ----------- | -------- 122 | type | The key in the `verifiable` object is the claims type requested | yes 123 | essential | This claim is essential. A response should not be returned if user does not have this claim | no 124 | reason | Short string explaining why you need this | no 125 | 126 | Per the OpenId spec if the value of the claim type in the object is `null` then it is a non essential value. 127 | 128 | Examples: 129 | 130 | ```js 131 | { 132 | email: { 133 | essential: true, 134 | reason: 'Whe need to be able to email you' 135 | }, 136 | name: null, 137 | phone: { 138 | reason: 'So we can notify you by text' 139 | } 140 | } 141 | ``` 142 | 143 | ## Unsigned Requests (Deprecated) 144 | 145 | To perform an unsigned selective disclosure request append the request parameters as URL encoded query parameters to one of the above endpoints and open it. Eg.: 146 | 147 | `me.uport:me?callback_url=https://mysite.com/callback&label=My%20Site` 148 | 149 | The following 150 | 151 | Name | Description | Required 152 | ---- | ----------- | -------- 153 | `callback_url` | The URL that receives the response | yes 154 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a HTTP POST or open the link (`redirect`). If unspecified, the mobile app will attempt to pick the correct one| no 155 | `client_id` | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the requesting identity | no 156 | `label` | Plain text name of client to be displayed to user | no 157 | `network_id` | Network ID of Ethereum chain of identity eg. `0x4` for `rinkeby` | no 158 | -------------------------------------------------------------------------------- /messages/shareresp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Off-chain Messages" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/shareresp.md" 6 | --- 7 | 8 | # Selective Disclosure Response 9 | 10 | The Selective Disclosure Response is created by a users mobile app as a response to a [Selective Disclosure Request](sharereq.md) during the [Selective Disclosure Flow](/flows/selectivedisclosure.md). 11 | 12 | The response is always signed and returned to the callback url included in the request. 13 | 14 | #### Attributes 15 | 16 | The following attributes of the JWT are supported: 17 | 18 | Name | Description | Required 19 | ---- | ----------- | -------- 20 | `type` | MUST have the value `shareResp` | yes 21 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the signing identity| yes 22 | [`aud`](https://tools.ietf.org/html/rfc7519#section-4.1.3) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the requester or the callback url if this was created as a response to an unsigned request | yes 23 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 24 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | yes 25 | `req`| The original JWT encoded Selective Disclosure Request | yes for responses to signed requests 26 | `nad`| The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the Ethereum account requested using `act` in the [Selective Disclosure Request](sharereq.md) | no 27 | `dad`| The `devicekey` as a regular hex encoded ethereum address as requested using `act='devicekey'` in the [Selective Disclosure Request](sharereq.md) | no 28 | `own` | The self signed claims requested from a user. Either as an Object of claim types for self signed claims eg: `{"name":"Carol Crypteau", "email":"carol@sample.com","image":{"/":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"}}` or the IPFS Hash of a JSON encoded equivalent. See [claims](/messages/claims.md) | no 29 | `verified\|vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent. See [Verified Claims](/messages/verification.md) | no 30 | `capabilities` | An array of JWT tokens giving client app the permissions requested. Currently a token allowing them to send push notifications | no 31 | `boxPub` | 32 byte base64 encoded [`Curve25519`](http://nacl.cr.yp.to/box.html) public key used for sending encrypted messages to user | no 32 | -------------------------------------------------------------------------------- /messages/signtypeddatareq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sign Typed Data Request" 3 | category: "messages" 4 | "type": "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/signtypeddatareq.md" 6 | --- 7 | 8 | # Sign Typed Data Request 9 | 10 | The Sign Typed Data Request is a message created by a client app and sent to a user's mobile app, which contains a potential complex claim to be signed. These claims can be signed and verified on-chain as well as off, and include in the object to be signed a definition of the structure of the object itself. For more information, see the [ERC712 specification for typed data](https://eips.ethereum.org/EIPS/eip-712). 11 | 12 | 13 | #### Attributes 14 | 15 | The JWT shares these attributes with the [Share Request](sharereq.md) and [Verification Request](verificationreq.md): `iat`, `exp`, `callback`, and `vc`; it also shares the `riss` field with [Verification Request](verificationreq.md). The claim in the `typedData` object should follow the [ERC712 Typed Data specification](https://eips.ethereum.org/EIPS/eip-712). 16 | 17 | The following additional attributes of the JWT are supported: 18 | 19 | Name | Description | Required 20 | ---- | ----------- | -------- 21 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 22 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 23 | `type` | MUST have the value `eip712Req` | yes 24 | `typedData` | A JSON object that conforms to the [ERC712 specification for typed data](https://eips.ethereum.org/EIPS/eip-712). It must contain the keys `types`, `primaryType`, `domain`, and `message`. | yes 25 | `callback` | Callback URL for returning the response to a request (may be deprecated in future) | no 26 | `riss` | The DID of the identity you want to sign the Verified Claim | no 27 | `from` | Hex encoded address requested to sign the transaction. If not specified the user will select an account. | no 28 | `net` | network id of Ethereum chain of identity eg. `0x4` for `rinkeby`. It defaults to `0x1` for `mainnet`. | no 29 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 30 | 31 | 32 | Example Typed Data Claim request: 33 | 34 | ```json 35 | { 36 | "riss":"did:uport:IDENTITY_THAT_WILL_SIGN_THE_CLAIM", 37 | "typedData": { 38 | "types": { 39 | "EIP712Domain": [ 40 | {"name": "name", "type": "string"}, 41 | {"name": "version", "type": "string"}, 42 | {"name": "chainId", "type": "uint256"}, 43 | {"name": "verifyingContract", "type": "address"}, 44 | {"name": "salt", "type": "bytes32"} 45 | ], 46 | "Greeting": [ 47 | {"name": "text", "type": "string"}, 48 | {"name": "subject", "type": "string"}, 49 | ] 50 | }, 51 | "domain": { 52 | "name": "My dapp", 53 | "version": "1.0", 54 | "chainId": 1, 55 | "verifyingContract": "0xdeadbeef", 56 | "salt": "0x999999999910101010101010" 57 | }, 58 | "primaryType": "Greeting", 59 | "message": { 60 | "text": "Hello", 61 | "subject": "World" 62 | } 63 | }, 64 | "callback": "https://example.com", 65 | "exp": 123456789 66 | } 67 | ``` -------------------------------------------------------------------------------- /messages/signtypeddataresp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sign Typed Data Response" 3 | category: "messages" 4 | "type": "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/signtypeddataresp.md" 6 | --- 7 | 8 | # Signed Typed Data Response 9 | 10 | A signed typed data response is a message created by a client app which includes a structured object that conforms to the EIP712 specification. In particular, the signed typed data response contains within it the typed data signature *request*, so that the signature may be verified by anyone who receives the response jwt. See also: [Sign Typed Data Request](signtypeddatareq.md) 11 | 12 | # Attributes 13 | 14 | The JWT contains three required fields, `iat`, `iss`, `request`, and `signature`. The `request` is an EIP712 signature request, as definied in the [EIP712 specification for typed data](https://eips.ethereum.org/EIPS/eip-712). `iss` and `iat` are defined as the issuer of the JWT, and the time at which it was issued, as usual. The `signature` field is an object containing the components `r`, `s`, and `v` which make up the signature over the typed data specified in the `request` field. The JWT may optionally also contain the `exp`, `vc` fields. 15 | 16 | Name | Description | Required 17 | ---- | ----------- | -------- 18 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 19 | `type` | MUST have the value `eip712Resp` | yes 20 | `iss` | The DID of the issuer of the JWT, not necessarily the same as the signer in `signature` | yes 21 | `request` | A JSON object that conforms to the [ERC712 specification for typed data](https://eips.ethereum.org/EIPS/eip-712). It must contain the keys `types`, `primaryType`, `domain`, and `message`. | yes 22 | `signature` | An object containing `r`, `s`, and `v`, the components of the signature | yes 23 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 24 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 25 | 26 | 27 | 28 | An example typed data signature response: 29 | ```json 30 | { 31 | "iat": 123456789, 32 | "iss": "did:ethr:0x...", 33 | "request": { 34 | "types": { 35 | "EIP712Domain": [ 36 | {"name": "name", "type": "string"}, 37 | {"name": "version", "type": "string"}, 38 | {"name": "chainId", "type": "uint256"}, 39 | {"name": "verifyingContract", "type": "address"}, 40 | {"name": "salt", "type": "bytes32"} 41 | ], 42 | "Greeting": [ 43 | {"name": "text", "type": "string"}, 44 | {"name": "subject", "type": "string"}, 45 | ] 46 | }, 47 | "domain": { 48 | "name": "My dapp", 49 | "version": "1.0", 50 | "chainId": 1, 51 | "verifyingContract": "0xdeadbeef", 52 | "salt": "0x999999999910101010101010" 53 | }, 54 | "primaryType": "Greeting", 55 | "message": { 56 | "text": "Hello", 57 | "subject": "World" 58 | } 59 | } 60 | }, 61 | "signature": { 62 | "r": "0x...", 63 | "s": "0x...", 64 | "v": "0x..." 65 | }, 66 | exp: 123456789, 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /messages/tx.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ethereum Transaction Request" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/tx.md" 6 | --- 7 | 8 | # Ethereum Transaction Request 9 | 10 | This message allows an application to request that a client signs an ethereum transaction. 11 | 12 | The Ethereum Transaction Request is created by a client app and sent to a user's mobile app as part of the [Ethereum Transaction Request Flow](../flows/tx.md). 13 | 14 | Requests can be either signed or unsigned. 15 | 16 | Unsigned Ethereum Transaction Requests are a superset of [ERC 67](https://github.com/ethereum/EIPs/issues/67) and are deprecated. 17 | 18 | ## Signed Transaction Request 19 | 20 | The Signed Transaction Request is a JWT (JSON Web Token) following the general conventions used throughout this spec for [signed messages](./index.md#json-web-token). 21 | 22 | It generally follows the parameters used in the standard [JSON-RPC `eth_sendTransaction()` call](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sendtransaction), making it simple for developers to integrate in their apis. 23 | 24 | ### Attributes 25 | 26 | The following attributes of the JWT are supported: 27 | 28 | Name | Description | Required 29 | ---- | ----------- | -------- 30 | `type` | MUST have the value `ethtx` | yes 31 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec) or [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the application identity requesting the signature| yes 32 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 33 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 34 | `from` | [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) or hex encoded address requested to sign the transaction. If not specified the user will select an account. | no 35 | `to` | [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) or hex encoded address of the recipient of the transaction. If not specified the transaction will create a contract and a bytecode field must exist. | no 36 | `net` | network id of Ethereum chain of identity eg. `0x4` for `rinkeby`. It defaults to the network encoded in the `to` if specified as an [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids). If not it defaults to `0x1` for `mainnet`. | no 37 | `value` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) in wei | no 38 | `fn` | Solidity function call eg. `transfer(address 0xdeadbeef, uint 5)` | no 39 | `gasPrice` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) of integer of the gasPrice used for each paid gas. This is only treated as a recommendation. The client may override this. | no 40 | `gas` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) of integer of the gas provided for the transaction execution. It will return unused gas. This is only treated as a recommendation. The client may change this. | no 41 | `data` | Hex encoded data field of transaction | no 42 | `callback` | The URL that receives the response | no 43 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a HTTP POST or open the link (`redirect`). If unspecified the mobile app will attempt to pick the correct one| no 44 | `client_id` | The [DID](https://w3c-ccg.github.io/did-spec) or [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the requesting identity | no 45 | `label` | Plain text name of client to be displayed to user | no 46 | `boxPub` | 32 byte base64 encoded [`Curve25519`](http://nacl.cr.yp.to/box.html) public key of requesting identity. Use to encrypt messages sent to callback URL| no 47 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 48 | 49 | The attributes `redirect_url` can also be appended to the signed request as URL encoded query parameters outside of the signed payload. They are used to specify how you want the response and control returned. For more details see [Messages](./index.md#json-web-token). 50 | 51 | 52 | ## ERC 67 Unsigned Transaction Request 53 | 54 | To perform an unsigned selective disclosure request append the request parameters as URL encoded query parameters to one of the above endpoints and open it. Eg.: 55 | 56 | `me.uport:2oDZvNUgn77w2BKTkd9qKpMeUo8EL94QL5V?transfer(address%200xdeadbeef%2C%20uint%205)&callback_url=https://mysite.com/callback&label=My%20Site` 57 | 58 | ### Attributes 59 | 60 | The following 61 | 62 | Name | Description | Required 63 | ---- | ----------- | -------- 64 | `from` | [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) or hex encoded address requested to sign the transaction. If not specified the user will select an account. | no 65 | `to` | This is the [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) or hex encoded address of the recipient of the transaction. If not specified the transaction will create a contract and a bytecode field must exist. | yes 66 | `net` | network id of Ethereum chain of identity eg. `0x4` for `rinkeby`. It defaults to the network encoded in the `to` if specified as an [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids). If not it defaults to `0x1` for `mainnet`. | no 67 | `value` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) in wei | no 68 | `function` | Solidity function call eg. `transfer(address 0xdeadbeef, uint 5)` | no 69 | `bytecode` | Hex encoded data field of transaction | no 70 | `gasPrice` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) of integer of the gasPrice used for each paid gas. This is only treated as a recommendation. The client may override this. | no 71 | `gas` | [hex encoded value](https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding) of integer of the gas provided for the transaction execution. It will return unused gas. This is only treated as a recommendation. The client may change this. | no 72 | `callback_url` | The URL that receives the response | no 73 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a HTTP POST or open the link (`redirect`). If unspecified the mobile app will attempt to pick the correct one| no 74 | `client_id` | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the requesting identity | no 75 | `label` | Plain text name of client to be displayed to user | no 76 | 77 | ## Addresses and Network selection 78 | 79 | Addresses SHOULD be either [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) encoded so they have the correct network specified with them or be a combination of a normal ethereum hex address and a `net` attribute specifying the network. 80 | 81 | If there is any kind of mismatch between `to`, `from` or `net` an error will be returned. 82 | 83 | ## Transaction Request Validity 84 | 85 | There are 3 types of transactions supported by Ethereum. The required fields depend on which kind of transaction you are requesting. 86 | 87 | ### ETH Value Transfer 88 | 89 | The following fields are required: 90 | 91 | - `to` 92 | - `value` 93 | 94 | ### ETH Value Transfer 95 | 96 | The following fields are required: 97 | 98 | - `to` 99 | 100 | Either of these two are required: 101 | 102 | - `data` or (`bytecode` for ERC 67) 103 | - `fn` or (`function` for ERC 67) 104 | 105 | ### Ethereum Smart Contract creation 106 | 107 | The following fields are required: 108 | 109 | - `data` or (`bytecode` for ERC 67) 110 | - `net` 111 | 112 | ## Client Callback 113 | 114 | The client app SHOULD include a URL where the response is returned from the user. This can be a https url or a custom app url which receives the response. 115 | 116 | Responses are param appended to a url fragment. If the callback requires the response as a HTTP POST, it is sent as a JSON POST request to the callback url instead. 117 | -------------------------------------------------------------------------------- /messages/verification.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Verified Claims" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/verification.md" 6 | --- 7 | 8 | 9 | # Verified Claims 10 | 11 | A Verified Claim is a statement issued by a third party verifying claims about an identity [Verified Claim Flow](../flows/verification.md). 12 | 13 | Verified Claims are always signed. 14 | 15 | ## Attributes 16 | 17 | The following attributes of the JWT are supported: 18 | 19 | Name | Description | Required 20 | ---- | ----------- | -------- 21 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the signing identity| yes 22 | [`sub`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The [DID](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the subject identity| yes 23 | `type`| The type of attestation | no 24 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 25 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of Verification | no 26 | `claim` | An object containing one or more claims about `sub` eg: `{"name":"Carol Crypteau"}` | yes 27 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message | no 28 | 29 | ## Claims Best Practices 30 | 31 | ### Use atomic claims 32 | 33 | In most cases we recommend having a single claim per Verified Claim. This allows your users to share only the specific claims they want. 34 | 35 | ```json 36 | {"name":"Carol Crypteau"} 37 | ``` 38 | 39 | ### Bundling multiple claims as one 40 | 41 | Some times multiple claims are actually a single claim. A good example is an address which can consist of multiple fields. In that case we recommend nesting them under a single claim like this: 42 | 43 | ```json 44 | {"address": { 45 | "streetAddress":"12345 Buterin Lane", 46 | "postalCode":"123133", 47 | "addressLocality":"Toronto", 48 | "addressRegion":"ON", 49 | "addressCountry":"CA"} 50 | } 51 | ``` 52 | 53 | ### Claim Taxonomy 54 | 55 | uPort is agnostic to different claim types. This allows you to plug in your own taxonomy of claims or use an existing taxonomy that works in your industry. 56 | 57 | If you don't need to follow a specific taxonomy of claims, we recommend that you find suitable claim types within [Schema.org](http://schema.org). 58 | -------------------------------------------------------------------------------- /messages/verificationreq.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Verification Claim Request" 3 | category: "messages" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/messages/verificationreq.md" 6 | --- 7 | 8 | # Verified Claim Request 9 | 10 | The Verified Claim Request is created by a client app and sent to a user's mobile app during the [Verified Claim Request Flow](/flows/verification.md). 11 | 12 | 13 | #### Attributes 14 | 15 | The JWT shares these attributes with the [Share Request](sharereq.md): `iss`, `iat`, `exp`. The claim in the `unsignedClaim` object should follow the [claims best practices](./verification.md#claims-best-practices). 16 | 17 | The following additional attributes of the JWT are supported: 18 | 19 | Name | Description | Required 20 | ---- | ----------- | -------- 21 | [`iss`](https://tools.ietf.org/html/rfc7519#section-4.1.1) | The DID of the signing identity| yes 22 | [`sub`](https://tools.ietf.org/html/rfc7519#section-4.1.2) | The DID of the identity you want the user to sign the claims ABOUT | no 23 | [`aud`](https://tools.ietf.org/html/rfc7519#section-4.1.3) | The DID or URL of the audience of the JWT. Our libraries or app will not accept any JWT that has someone else as the audience| no 24 | [`iat`](https://tools.ietf.org/html/rfc7519#section-4.1.6) | The time of issuance | yes 25 | [`exp`](https://tools.ietf.org/html/rfc7519#section-4.1.4) | Expiration time of JWT | no 26 | `type` | MUST have the value `verReq` | yes 27 | `unsignedClaim` | An unsigned claim that the user is requested to sign, this should exactly match the `claim` of the finished signed [Verified Claim](./verification.md). | yes 28 | `callback` | Callback URL for returning the response to a request (may be deprecated in future) | no 29 | `riss` | The DID of the identity you want to sign the Verified Claim | no 30 | `rexp` | Requested expiry time in seconds | no 31 | `vc` | Array of Verified Claims JWTs or IPFS hash of JSON encoded equivalent about the `iss` of this message. See [Issuer Claims](/messages/claims.md) and [Verified Claims](/messages/verification.md) | no 32 | 33 | 34 | Example Verified Claim request: 35 | 36 | ```js 37 | { 38 | "type":"verReq", 39 | "iss":"did:uport:REQUESTING_APP_OR_USER", 40 | "aud":"did:uport:VERIFYING_APP_OR_USER", 41 | "sub":"did:uport:SUBJECT_OF_VERIFIED_CLAIM", 42 | "riss":"did:uport:IDENTITY_THAT_WILL_SIGN_THE_CLAIM", 43 | "unsignedClaim": { 44 | "name":"Bob Smith" 45 | }, 46 | "callback":"https://example.com", 47 | "rexp": 123456789 48 | } 49 | ``` 50 | 51 | The verifying user views the requested data in the UX and signs the corresponding [Verified Claim](./verification.md). The following is an example of the response: 52 | 53 | ```js 54 | { 55 | "iss":"did:uport:VERIFYING_APP_OR_USER", 56 | "sub":"did:uport:SUBJECT_OF_VERIFIED_CLAIM", 57 | "claim": { 58 | "name":"Bob Smith" 59 | }, 60 | "exp": 123456789 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /pki/diddocument.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Decentralized Identity Document" 3 | category: "pki" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/pki/diddocument.md" 6 | --- 7 | 8 | # Decentralized Identity Document (DID Document) 9 | 10 | This is the subset of the [DID Document](https://w3c-ccg.github.io/did-spec) spec that we implement as part of the uPort platform. 11 | 12 | The Identity document is stored on IPFS and tied to the address using the uport registry as specified in the [PKI document](./index.md). 13 | 14 | ## Contents 15 | 16 | The Identity document must contain the public key for the identity, everything else is optional. Anything in this document is public, so please be wary of publishing any Private information to it. 17 | 18 | This is an example of a minimal identity document: 19 | 20 | ```js 21 | { 22 | "@context": "https://w3id.org/did/v1", 23 | "id": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX", 24 | "publicKey": [{ 25 | "id": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-1", 26 | "type": "Secp256k1VerificationKey2018", 27 | "owner": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX", 28 | "publicKeyHex": "04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062" 29 | }, { 30 | "id": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-2", 31 | "type": "Curve25519EncryptionPublicKey", 32 | "owner": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX", 33 | "publicKeyBase64": "QCFPBLm5pwmuTOu+haxv0+Vpmr6Rrz/DEEvbcjktQnQ=" 34 | }], 35 | "authentication": [{ 36 | "type": "Secp256k1SignatureAuthentication2018", 37 | "publicKey": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-1" 38 | }] 39 | } 40 | ``` 41 | 42 | This is a example of a identity document for an app with extra public profile information: 43 | 44 | ```js 45 | { 46 | "@context": "https://w3id.org/did/v1", 47 | "id": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX", 48 | "publicKey": [{ 49 | "id": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-1", 50 | "type": "Secp256k1VerificationKey2018", 51 | "owner": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX", 52 | "publicKeyHex": "04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062" 53 | }], 54 | "authentication": [{ 55 | "type": "Secp256k1SignatureAuthentication2018", 56 | "publicKey": "did:uport:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX#keys-1" 57 | }], 58 | "uportProfile": { 59 | "@context":"http://schema.org", 60 | "@type":"Organization", 61 | "name":"uPort @ Devcon 3", 62 | "description":"Uport Attestations", 63 | "image":{"@type":"ImageObject","name":"avatar","contentUrl":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"} 64 | } 65 | } 66 | 67 | ``` 68 | 69 | ### Parameters 70 | 71 | Name | Description | Required 72 | ---- | ----------- | -------- 73 | `@context` | `https://w3id.org/did/v1`| yes 74 | `publicKey` | array of allowed [public key objects](https://w3c-ccg.github.io/did-spec/#public-keys) containing hex encoded [secp256k1 ECDSA curve](https://en.bitcoin.it/wiki/Secp256k1) public keys and/or Ed25519| yes 75 | `authentication` | array of [authentication objects](https://w3c-ccg.github.io/did-spec/#authentication) specifying which keys are allowed to be used for authenticating the identity owner | yes 76 | `owner` | Public profile information compatible with [schema.org](https://schema.org) | no 77 | -------------------------------------------------------------------------------- /pki/identitydocument.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Identity Document" 3 | category: "pki" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/pki/identitydocument.md" 6 | --- 7 | 8 | # Identity Document (DEPRECATED) 9 | 10 | Note this format will be deprecated soon and replaced by a standard [DID Document](./diddocument.md). 11 | 12 | The Identity document is stored on IPFS and tied to the address using the uport registry as specified in the [PKI document](../index). 13 | 14 | The Identity document is a JSON document (strictly speaking a [JSON-LD](https://json-ld.org/)). 15 | 16 | ## Contents 17 | 18 | The Identity document must contain the public key for the identity, everything else is optional. Anything in this document is public, so please be wary of publishing any Private information to it. 19 | 20 | This is an example of a minimal identity document: 21 | 22 | ```js 23 | { 24 | "@context":"http://schema.org", 25 | "@type":"Person", 26 | "publicKey":"0x04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062", 27 | "publicEncKey":"QCFPBLm5pwmuTOu+haxv0+Vpmr6Rrz/DEEvbcjktQnQ=" 28 | } 29 | ``` 30 | 31 | This is a example of a identity document for an app with extra public profile information: 32 | 33 | ```js 34 | { 35 | "@context":"http://schema.org", 36 | "@type":"Organization", 37 | "name":"uPort @ Devcon 3", 38 | "description":"Uport Attestations","publicKey":"0x04613bb3a4874d27032618f020614c21cbe4c4e4781687525f6674089f9bd3d6c7f6eb13569053d31715a3ba32e0b791b97922af6387f087d6b5548c06944ab062", 39 | "image":{"@type":"ImageObject","name":"avatar","contentUrl":"/ipfs/QmSCnmXC91Arz2gj934Ce4DeR7d9fULWRepjzGMX6SSazB"} 40 | } 41 | ``` 42 | 43 | ### Parameters 44 | 45 | Name | Description | Required 46 | ---- | ----------- | -------- 47 | `@context` | `http://schema.org`| yes 48 | `@type` | `Person`, `App`, `Organization`| yes 49 | `publicKey` | `0x` prefixed hex encoded [secp256k1 ECDSA curve](https://en.bitcoin.it/wiki/Secp256k1) public key | yes 50 | `publicEncKey` | Base64 encoded [`curve25519xsalsa20poly1305`](http://nacl.cr.yp.to/box.html) public key | no 51 | `name` | Name of identity | no 52 | `description` | Description of identity | no 53 | `image` | Avatar or logo of identity (Uses [ImageObject](http://schema.org/ImageObject) with `contentUrl`) | no 54 | 55 | ### Resolving the Public Key for `iss` 56 | 57 | ![Resolve Public Key](resolve.png) 58 | 59 | 1. Decode the MNID of the `iss` and extract the `network` and the `address` 60 | 2. In the [uport-registry](https://github.com/uport-project/uport-registry) for the `network` call the function `get("uPortProfileIPFS1220", address, address)` which returns a hash value encoded as 32 bytes 61 | 3. Encode the IPFS hash by prepending hex `1220` to the 32 byte hash and encoding it as base58 62 | 4. Fetch [JSON Identity Document](./identitydocument.md) from IPFS using IPFS hash 63 | 5. Public Key is stored in the `publicKey` key of the Identity Document 64 | 65 | ### Resolving the Public Encryption Key for `iss` 66 | 67 | Done in the same way as above except for the last step: 68 | 69 | 5. Public Key is stored in the `publicEncKey` key of the [Identity Document](./identitydocument.md) 70 | 71 | ## Registering an Identity Document 72 | 73 | Any address on any supported Ethereum blockchain can register its identity document on the [uport-registry](https://github.com/uport-project/uport-registry). 74 | 75 | This shows the basic process: 76 | 77 | ![Identity Registration](registration.png) 78 | 79 | ### External Accounts (Key Pairs) 80 | 81 | 1. Generate a Key Pair 82 | 1. Create a [Identity Document](./identitydocument.md) containing the Public Key 83 | 1. Publish Identity Document to IPFS 84 | 1. Decode IPFS hash returned to get the raw 32 byte hash value 85 | 1. Pick an Ethereum network to register your identity on 86 | 1. Generate the Ethereum address for your Key Pair 87 | 1. Create a transaction in the [uport-registry](https://github.com/uport-project/uport-registry) for the `network` for the function `set("uPortProfileIPFS1220", address, hash)` signed by your Key Pair 88 | 89 | ### Smart Contract Accounts 90 | 91 | Smart contracts can't sign on their own, so a signing Key Pair will need to be created first. 92 | 93 | 1. Generate a Key Pair 94 | 1. Create an Identity Document containing the Public Key 95 | 1. Publish Identity Document to IPFS 96 | 1. Decode IPFS hash returned to get the raw 32 byte hash value 97 | 1. With your smart contract code create an internal transaction to the [uport-registry](https://github.com/uport-project/uport-registry) on the `network` that your smart contract is deployed to for the function `set("uPortProfileIPFS1220", address, hash)` 98 | 99 | Here is an example of how to register an Identity for your smart contract in Solidity: 100 | 101 | ```js 102 | contract Registry { function set(bytes32 key, address subject, bytes32 value); } 103 | 104 | contract MyContract { 105 | address public owner; 106 | Registry registry; 107 | 108 | function MyContract(address _registry) { 109 | owner = msg.sender; 110 | registry = Registry(_registry); 111 | } 112 | 113 | function setIdentityDoc(bytes32 hash) { 114 | // Only allow owner of contract to set the identity document. 115 | // There could of course be more advanced governance mechanisms here. 116 | require(msg.sender == owner); 117 | 118 | registry.set("uPortProfileIPFS1220", this, hash); 119 | } 120 | } 121 | ``` 122 | 123 | ### uPort Mobile App Created Identities 124 | 125 | Identities created by the uPort Mobile App consist of a simple [Proxy](https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol) smart contract controlled by a flexible access control smart contract that we call the [IdentityManager](https://github.com/uport-project/uport-identity/blob/develop/contracts/IdentityManager.sol). 126 | 127 | This structure allows us to create recoverable identities controlled by multiple devices and even allows us to safely upgrade the complex access control logic. 128 | 129 | The way an identity is created in the Mobile App is as follows: 130 | 131 | ![Mobile Identity Registration](mobileregistration.png) 132 | 133 | 1. Generate a Key Pair on your uPort app 134 | 1. Pick an Ethereum network to register your identity on 135 | 1. Create an Ethereum transaction registering a [Proxy](https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol) using the [IdentityManager](https://github.com/uport-project/uport-identity/blob/develop/contracts/IdentityManager.sol) 136 | 1. Create an Identity Document containing the Public Key 137 | 1. Publish Identity Document to IPFS 138 | 1. Decode IPFS hash returned to get the raw 32 byte hash value 139 | 1. Create a transaction on the IdentityManager that forwards a transaction to the [uport-registry](https://github.com/uport-project/uport-registry) calling the function `set("uPortProfileIPFS1220", address, hash)` signed by your Key Pair 140 | -------------------------------------------------------------------------------- /pki/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Uport PKI" 3 | category: "pki" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/pki/index.md" 6 | --- 7 | 8 | # uPort Public Key Infrastructure (PKI) 9 | 10 | uPort implements a simple yet general purpose decentralized PKI system, making it easy to create and verify off-chain JWT messages. 11 | 12 | ## Purpose 13 | 14 | We need a decentralized way to lookup public keys that can be used to verify off-chain JWTs. This allows us to use the power of the Ethereum blockchain to verify signed data privately transferred between parties. 15 | 16 | The PKI is not needed for blockchain transactions themselves, as any blockchain already has a PKI-like functionality built in. 17 | 18 | We are primarily using it with JWTs, although it could be used for signing other data formats as well. 19 | 20 | ## Creating and Verifying a JWT 21 | 22 | The following overview shows the basic process for creating and verifying a trusted off-chain transaction between two parties using the uPort PKI. 23 | 24 | ![Create and Verify Data](jwtflow.png) 25 | 26 | ## Identity Document 27 | 28 | We currently support 2 kinds of Identity Documents: 29 | 30 | - [DID Documents](./diddocument.md) 31 | - [Legacy Identity Documents](./identitydocument.md) (DEPRECATED) 32 | 33 | ## Verifying a signature 34 | 35 | Any [Signed Message](../messages/index.md) has an `iss` attribute. This contains an [Decentralized ID (DID)](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids). 36 | 37 | A [did-resolver](https://github.com/uport-project/did-resolver) is used to resolve the public key of the message. 38 | 39 | uPort currently supports the following DID methods: 40 | 41 | - [`ethr`](https://github.com/uport-project/ethr-did-resolver) based on [ERC-1056](https://github.com/ethereum/EIPs/issues/1056) 42 | - [`uport`](https://github.com/uport-project/uport-did-resolver) for [legacy uPort identities](./identitydocument.md) 43 | - [`muport`](https://github.com/uport-project/muport-did-resolver) for an experimental did resolver using IPFS and Ethereum 44 | 45 | Anyone implementing a new DID method can follow the instructions in the [did-resolver](https://github.com/uport-project/did-resolver) library and it should work with uPort libraries. 46 | -------------------------------------------------------------------------------- /pki/jwtflow.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Identity Resolving 4 | actor Sender 5 | participant IdentityWallet 6 | database UportRegistry 7 | database IPFS 8 | actor Recipient 9 | 10 | Sender -> IdentityWallet : Sign JWT 11 | IdentityWallet -> Sender: Signed JWT 12 | Sender -> Recipient: Signed JWT 13 | Recipient <-> UportRegistry: Lookup IPFS hash for iss 14 | Recipient <-> IPFS: Identity Document for IPFS hash 15 | Recipient -> Recipient: Extract PublicKey from Identity Document 16 | Recipient -> Recipient: Verify signature using PublicKey 17 | @enduml 18 | -------------------------------------------------------------------------------- /pki/jwtflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/pki/jwtflow.png -------------------------------------------------------------------------------- /pki/mobileregistration.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Generic Identity Registration 4 | actor Owner 5 | participant UportApp 6 | participant IdentityManager 7 | database UportRegistry 8 | database IPFS 9 | 10 | Owner -> UportApp : Create Identity 11 | UportApp -> UportApp: Generate Key Pair 12 | UportApp -> UportApp: Store private key securely 13 | UportApp -> UportApp: Generate Address 14 | UportApp -> IdentityManager: Generate Identity SmartContract on Ethereum 15 | UportApp -> IPFS: Generate Identity Document containing PublicKey 16 | IPFS -> UportApp: Return IPFS hash of Identity Document 17 | UportApp -> IdentityManager: Request IdentityManager forward registration transaction 18 | IdentityManager -> UportRegistry: Ethereum transaction registering IPFS hash 19 | @enduml 20 | -------------------------------------------------------------------------------- /pki/mobileregistration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/pki/mobileregistration.png -------------------------------------------------------------------------------- /pki/registration.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Generic Identity Registration 4 | actor Owner 5 | participant IdentityWallet 6 | database UportRegistry 7 | database IPFS 8 | 9 | Owner -> IdentityWallet : Create Identity 10 | IdentityWallet -> IdentityWallet: Generate Key Pair 11 | IdentityWallet -> IdentityWallet: Store private key securely 12 | IdentityWallet -> IdentityWallet: Generate Address 13 | IdentityWallet -> IPFS: Generate Identity Document containing PublicKey 14 | IPFS -> IdentityWallet: Return IPFS hash of Identity Document 15 | IdentityWallet -> UportRegistry: Ethereum transaction registering IPFS hash 16 | 17 | @enduml 18 | -------------------------------------------------------------------------------- /pki/registration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/pki/registration.png -------------------------------------------------------------------------------- /pki/registration.svg: -------------------------------------------------------------------------------- 1 | Generic Identity RegistrationOwnerOwnerIdentityWalletIdentityWalletUportRegistryUportRegistryIPFSIPFSCreate IdentityGenerate Key PairStore private key securelyGenerate AddressGenerate Identity Document containing PublicKeyReturn IPFS hash of Identity DocumentEthereum transaction registering IPFS hash -------------------------------------------------------------------------------- /pki/resolve.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Resolving PublicKey 4 | actor Recipient 5 | database UportRegistry 6 | database IPFS 7 | 8 | Recipient <-> UportRegistry: Lookup IPFS hash for Sender 9 | Recipient <-> IPFS: Identity Document for IPFS hash 10 | Recipient -> Recipient: Extract PublicKey from Identity Document 11 | @enduml 12 | -------------------------------------------------------------------------------- /pki/resolve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/pki/resolve.png -------------------------------------------------------------------------------- /pki/resolve.svg: -------------------------------------------------------------------------------- 1 | Resolving PublicKeyRecipientRecipientUportRegistryUportRegistryIPFSIPFSLookup IPFS hash for SenderIdentity Document for IPFS hashExtract PublicKey from Identity Document -------------------------------------------------------------------------------- /rest-apis/fuel-server.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Transaction Fueling Server" 3 | category: "apis" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/rest-apis/fuel-server.md" 6 | --- 7 | 8 | # Transaction Fueling Server 9 | 10 | All transactions on Ethereum like networks are paid using transaction fees known as `gas`. To avoid the requirement for all end users to fund themselves with ETH on their respective networks we support a transaction fueling service, which will be called if the users account does not have enough funds available to fuel the transaction 11 | 12 | ## API Description 13 | 14 | ### Fuel Token 15 | 16 | A JWT or similar Bearer token (see [RFC 6750](https://tools.ietf.org/html/rfc6750)) SHOULD be issued as part of the [Private Chain Provisioning Flow](/flows/privatechain.md). 17 | 18 | Funding service SHOULD verify based on their own business rules that the signer of the transaction is allowed to use the Fuel Token. 19 | 20 | ### Fund address 21 | 22 | This endpoints sends funds to the signing address of the included transaction. Note the funding server does not send the `tx` onto the network. This is the responsibility of the Uport Mobile App. 23 | 24 | #### Endpoint 25 | 26 | `POST /api/v1/fund/` 27 | 28 | #### Header 29 | 30 | ``` 31 | Authorization: Bearer 32 | ``` 33 | 34 | #### Body 35 | 36 | ``` 37 | { 38 | tx: , 39 | blockchain: 40 | } 41 | ``` 42 | 43 | #### Response 44 | 45 | | Status | Message | | 46 | |:------:|----------------|-------------------------------| 47 | | 200 | Ok. | address funded | 48 | | 403 | Forbidden | JWT token missing or invalid | 49 | | 404 | Not found | Blockchain not found | 50 | | 429 | Too many conns | Too many connections | 51 | | 500 | Internal Error | Internal error | 52 | 53 | #### Response data 54 | 55 | The transaction hash of the funding transaction: 56 | 57 | ``` 58 | { 59 | txHash: 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /rest-apis/relay-server.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Meta Transaction Relaying Server" 3 | category: "apis" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/rest-apis/relay-server.md" 6 | --- 7 | 8 | # Meta Transaction Relaying Server 9 | 10 | All transactions on Ethereum like networks are paid using transaction fees known as `gas`. To avoid the requirement for all end users to fund themselves with ETH on their respective networks we support [meta transactions](https://medium.com/uport/making-uport-smart-contracts-smarter-part-3-fixing-user-experience-with-meta-transactions-105209ed43e0) which allows a 3rd party transaction fueling service to fund and relay transactions without requiring pre-funding the users account. 11 | 12 | ## API Description 13 | 14 | ### Fuel Token 15 | 16 | A JWT or similar Bearer token (see [RFC 6750](https://tools.ietf.org/html/rfc6750)) SHOULD be issued as part of the [Private Chain Provisioning Flow](/flows/privatechain.md). 17 | 18 | Funding service SHOULD verify based on their own business rules that the signer of the transaction is allowed to use the Fuel Token. 19 | 20 | ### MetaSignedTx 21 | 22 | To create a meta transaction, you need the following parameters: 23 | 24 | - `txRelayAddress` address of the [txRelay](https://github.com/uport-project/uport-identity/blob/develop/contracts/TxRelay.sol#L28) contract 25 | - `whitelistOwner` (use `0x0000000000000000000000000000000000000000`) 26 | - `metaNonce` the [nonce stored in the txRelay contract](https://github.com/uport-project/uport-identity/blob/develop/contracts/TxRelay.sol#L11). This is NOT the same as the Ethereum account nonce 27 | - `to` ethereum address of recipient of transaction 28 | - `data` the data field of the ethereum transaction 29 | 30 | ```js 31 | metaTxInput = '1900' + stripHexPrefix(txRelayAddress) 32 | + stripHexPrefix(whitelistOwner) + padTo64(metaNonce) + to + data 33 | signature = secp256k1(keccak256(metaTxInput)) 34 | ``` 35 | 36 | Create an unsigned wrapper transaction to the [`relayMetaTx`](https://github.com/uport-project/uport-identity/blob/develop/contracts/TxRelay.sol#L28) function. 37 | 38 | ```js 39 | let wrapperTx = { 40 | 'gasPrice': raw_gasPrice, // ignored 41 | 'gasLimit': raw_gasLimit, // ignored 42 | 'value': 0, 43 | 'to': this.txRelayAddress 44 | } 45 | let rawMetaSignedTx = txutils.functionTx(txRelayAbi, 'relayMetaTx', [ 46 | signature.v, 47 | addHexPrefix(Buffer.from(signature.r, 'base64').toString('hex')), 48 | addHexPrefix(Buffer.from(signature.s, 'base64').toString('hex')), 49 | raw_to, 50 | raw_data, 51 | addHexPrefix(this.whitelistOwner) 52 | ], wrapperTx) 53 | ``` 54 | 55 | #### Endpoint 56 | 57 | `POST /api/v2/relay/` 58 | 59 | #### Header 60 | 61 | ``` 62 | Authorization: Bearer 63 | ``` 64 | 65 | #### Body 66 | 67 | ``` 68 | { 69 | metaSignedTx: , 70 | blockchain: , 71 | metaNonce: 72 | } 73 | ``` 74 | 75 | #### Response 76 | 77 | | Status | Message | | 78 | |:------:|----------------|-------------------------------| 79 | | 200 | Ok. | address funded | 80 | | 403 | Forbidden | JWT token missing or invalid | 81 | | 404 | Not found | Blockchain not found | 82 | | 429 | Too many conns | Too many connections | 83 | | 500 | Internal Error | Internal error | 84 | 85 | #### Response data 86 | 87 | The transaction hash of the real transaction on the blockchain: 88 | 89 | ``` 90 | { 91 | txHash: 92 | } 93 | ``` 94 | -------------------------------------------------------------------------------- /rest-apis/request-server.md: -------------------------------------------------------------------------------- 1 | # Request Response Server 2 | 3 | This RESTful API simplifies a few interactions for a serverless Decentralized Apps: 4 | 5 | - initiate a request via a compact QR code 6 | - receive a response from client 7 | 8 | uPort operates a free implementation of this service at this URL: 9 | 10 | `https://api.uport.me/chasqui` 11 | 12 | The [source is available on github](https://github.com/uport-project/lambda-chasqui) and you can freely run your own version of it. 13 | 14 | ## Initiating a request for use in a QR code 15 | 16 | Prepare the [Request Message JWT](/messages/index.md). This SHOULD NOT contain any private information. 17 | 18 | Just perform a HTTP `DELETE` on the callback url. 19 | 20 | #### Endpoint 21 | 22 | `POST /topic` 23 | 24 | #### Body 25 | 26 | ```js 27 | {"request":"JWT OF REQUEST"} 28 | ``` 29 | 30 | #### Response 31 | 32 | | Status | Message | | 33 | |:------:|----------------|--------------------------------------------| 34 | | 201 | Created | Topic created | 35 | | 500 | Internal Error | Internal Error | 36 | 37 | If successful (HTTP code 201) the topic URL to include in the QR code is included in the `Location` HTTP response header. 38 | 39 | ### End User Client Scanning QR code 40 | 41 | If the QR code contains a URL that does not match an existing deep URL used by the mobile client, it will attempt to fetch the request from it. 42 | 43 | #### Endpoint 44 | 45 | `GET /topic/:id` 46 | 47 | #### Response 48 | 49 | | Status | Message | | 50 | |:------:|----------------|-------------------------------| 51 | | 200 | Ok. | Message stored on topic | 52 | | 500 | Internal Error | Internal error | 53 | 54 | #### Response data 55 | 56 | The following is the request made by the mobile client. 57 | 58 | ```json 59 | { 60 | "status": "success", 61 | "message": { 62 | "request": "JWT OF REQUEST" 63 | }, 64 | "referrerUrl": "https://mydapp.com" 65 | } 66 | ``` 67 | 68 | ## Receiving a response from client 69 | 70 | The message sent as a response SHOULD be encrypted. See the [message encryption](/messages/encrypted.md) document for more details. 71 | 72 | ### Preparing callback URL 73 | 74 | To use the messaging server, create a large secure URL safe random number that we call the topic id. 75 | 76 | Include the callback URL with the following format in your request `https://api.uport.me/chasqui/topic/[TOPIC ID]`. 77 | 78 | ### Listening for Response 79 | 80 | You can perform polling to the same callback URL you passed along to the request using HTTP GET. 81 | 82 | #### Endpoint 83 | 84 | `GET /topic/:id` 85 | 86 | #### Response 87 | 88 | | Status | Message | | 89 | |:------:|----------------|-------------------------------| 90 | | 200 | Ok. | Message stored on topic | 91 | | 500 | Internal Error | Internal error | 92 | 93 | #### Response data 94 | 95 | If no response has been received the `message` object will be empty: 96 | 97 | ```json 98 | { 99 | "status": "success", 100 | "message": {} 101 | } 102 | ``` 103 | 104 | Once the uPort app returns the response it will be included there: 105 | 106 | ```json 107 | { 108 | "status": "success", 109 | "message": {"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJp..."} 110 | } 111 | ``` 112 | 113 | ### Cleanup 114 | 115 | To avoid having potentially private data stored on our server please `DELETE` the response after receiving a successful response. 116 | 117 | Just perform a HTTP `DELETE` on the callback url. 118 | 119 | #### Endpoint 120 | 121 | `DELETE /topic/:id` 122 | 123 | #### Response 124 | 125 | | Status | Message | | 126 | |:------:|----------------|--------------------------------------------| 127 | | 200 | Ok. | Topic deleted | 128 | | 404 | Not found | Topic not found | 129 | | 500 | Internal Error | Internal Error | 130 | -------------------------------------------------------------------------------- /transports/desktopdapp.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Desktop Browser dApp to Uport Mobile Request Flow 4 | participant ClientApp 5 | participant MessagingServer 6 | participant UportMobile 7 | actor Owner 8 | 9 | ClientApp -> Owner : Show QR code containing request 10 | Owner -> UportMobile: Scans QR code 11 | UportMobile -> Owner : Authorize Request? 12 | Owner -> UportMobile: Allow/Disallow 13 | UportMobile -> MessagingServer: Send response 14 | MessagingServer -> ClientApp: Receive Response 15 | 16 | @enduml 17 | -------------------------------------------------------------------------------- /transports/desktopdapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/transports/desktopdapp.png -------------------------------------------------------------------------------- /transports/desktopdapp.svg: -------------------------------------------------------------------------------- 1 | Desktop Browser dApp to Uport Mobile Request FlowClientAppClientAppMessagingServerMessagingServerUportMobileUportMobileOwnerOwnerShow QR code containing requestScans QR codeAuthorize Request?Allow/DisallowSend responseReceive Response -------------------------------------------------------------------------------- /transports/desktopserverapp.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Desktop Web Server App to Uport Mobile Request Flow 4 | participant ClientBackEnd 5 | participant ClientFrontEnd 6 | participant UportMobile 7 | actor Owner 8 | 9 | ClientFrontEnd <- ClientBackEnd : Create Signed Request 10 | ClientFrontEnd -> Owner : Show QR code containing request 11 | Owner -> UportMobile: Scans QR code 12 | UportMobile -> Owner : Authorize Request? 13 | Owner -> UportMobile: Allow/Disallow 14 | UportMobile -> ClientBackEnd: Send response 15 | ClientBackEnd -> ClientFrontEnd: Handle response 16 | 17 | @enduml 18 | -------------------------------------------------------------------------------- /transports/desktopserverapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/transports/desktopserverapp.png -------------------------------------------------------------------------------- /transports/desktopserverapp.svg: -------------------------------------------------------------------------------- 1 | Desktop Web Server App to Uport Mobile Request FlowClientBackEndClientBackEndClientFrontEndClientFrontEndUportMobileUportMobileOwnerOwnerCreate Signed RequestShow QR code containing requestScans QR codeAuthorize Request?Allow/DisallowSend responseHandle response -------------------------------------------------------------------------------- /transports/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Request/Response Transports" 3 | category: "transports" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/transports/index.md" 6 | --- 7 | 8 | # Request/Response Transports 9 | 10 | ## Requests 11 | 12 | Requests always consist of URLs that are handled by the mobile app. There are different built in ways of sending the URL to the mobile app. 13 | 14 | The 3 basic ways of sending a request to the phone are: 15 | 16 | - Open uPort URL on the phone 17 | - Scan QR Code 18 | - Send Push Notification 19 | 20 | For all of these cases the request consists of a URL. 21 | 22 | ### Open URL 23 | 24 | On the device any URL whose scheme is `me.uport:` or hostname is `id.uport.me` will be opened directly in the uPort app if installed. 25 | 26 | The benefit of using `https://id.uport.me` URLs is that they will open a web site with an App Store link if the uPort app is not installed. On a desktop browser, the browser will display a QR code containing the request and will ask the user to scan the code with their mobile app. 27 | 28 | The basic purpose of these URLs is to create links in a mobile web app containing requests to the uPort App. 29 | 30 | iOS developers can use [`open()`](https://developer.apple.com/documentation/uikit/uiapplication/1648685-open) with the URL directly. 31 | 32 | Android developers can use the [`ACTION_VIEW` intent](https://developer.android.com/reference/android/content/Intent.html#ACTION_VIEW) to open the URL as well. 33 | 34 | In addition to this basic interaction pattern, URLs starting with `https://id.uport.me` can be sent as tweets, in messages, emails or any other way that you interact with your users. 35 | 36 | ### QR Code 37 | 38 | By encoding the request in a QR code it is very easy for users with the uPort app installed to scan a request. On iOS 11 you can also scan these using the system camera and the app will open. 39 | 40 | While this is often used for interacting with an application in a desktop browser, there other applications: QR codes can be printed or displayed at conferences, on posters, or in other real-world use cases. 41 | 42 | Large requests create large QR codes that can be difficult to scan. Use the [Initiating a request for use in a QR code from the request server](/rest-apis/request-server.md) to create a compact URL containing the request. 43 | 44 | ### Push Notifications 45 | 46 | As part of a regular [Selective Disclosure Flow](/flows/selectivedisclosure.md) you can request permissions from your user to send requests directly to their uPort app using push notifications. 47 | 48 | Push notifications makes the interaction flow much simpler for users if they have to interact with multiple requests on their phone. 49 | 50 | They can also be used to send [Verifications](/flows/verification.md) or [Ethereum Transaction Requests](/flows/tx.md) directly to the user outside of a regular logged in session based on some external event. 51 | 52 | For more details see [Push Notification Transport](./push.md) 53 | 54 | ## Responses 55 | 56 | Responses are sent to the callback url included with the Request. 57 | 58 | Name | Description | Required 59 | ---- | ----------- | -------- 60 | `callback_url` | The URL that receives the response | no 61 | `callback_type` | Valid values `post` or `redirect`. Determines if callback should be sent as a 62 | 63 | For signed [Selective Disclosure Requests](/messages/sharereq.md) you should include the `callback` attribute in the JWT to ensure that it is not modified by malicious code. 64 | 65 | If no callback_url is specified then no response is returned. 66 | 67 | If no `callback_type` is specified the mobile app will attempt to pick the correct one: 68 | 69 | - If request was received as an Open Link it will default to `redirect` 70 | - If request was either scanned as a QR code or received as a push notification it assumes `post` 71 | 72 | ### Redirect callback type 73 | 74 | In this case the callback url is opened by the uPort mobile app with the response parameters added to the fragment component using the "application/x-www-form-urlencoded" format. 75 | 76 | ### HTTP post callback type 77 | 78 | The response is encoded as JSON and sent as an HTTP POST to the callback url provided. 79 | 80 | The callback MUST return 200 to notify the user it has been received correctly. 81 | 82 | [uPort has a request/response messaging server that can be used to automatically handle this for Serverless apps](/rest-apis/request-server.md). 83 | 84 | ## Examples 85 | 86 | ### Mobile Browser Transport 87 | 88 | In the case of a mobile app or a web app running in a mobile web browser, the request looks like this in more detail: 89 | 90 | ![Mobile Transport](mobile.png) 91 | 92 | - Client App opens the request URL directly and app opens 93 | - Response is "redirected" back to client app using the callback URL included. Original app opens and handles response. 94 | 95 | ### Desktop Browser Serverless Transport 96 | 97 | For web apps running in a desktop browser with no server backing it, the request and response transport looks like this: 98 | 99 | ![Desktop Serverless App Flow](desktopdapp.png) 100 | 101 | - The request URL is displayed as a QR code in desktop browser 102 | - User scans QR code using uPort mobile app 103 | - Response is sent back to desktop browser using an external messaging server 104 | 105 | ### Desktop Browser Server Backed Transport 106 | 107 | For web apps running in a desktop browser with an application server, the request and response transport looks like this: 108 | 109 | ![Desktop Server Backed Flow](desktopserverapp.png) 110 | 111 | - The request URL is displayed as a QR code in desktop browser 112 | - User scans QR code using uPort mobile app 113 | - Response is sent directly to back end server, which communicates with App front end in desktop browser 114 | 115 | ### Push Notification Transport 116 | 117 | Any of the above transports can be augmented with our Push Notification Transport mechanism. [We have a detailed article explaining how it works](https://medium.com/uport/adventures-in-decentralized-push-notifications-3c64e700ec18). 118 | 119 | From a protocol point of view it works like this: 120 | 121 | ![Push Notification Flow](push.png) 122 | 123 | - The user performs a regular [Selective Disclosure Flow](/flows/selectivedisclosure.md) asking for notification permissions using whichever transport they want 124 | - User authorizes the issuance of a "PushToken" to the client app 125 | - Client App receives response containing PushToken 126 | - All future requests are sent to a Push Server maintained by uPort authenticated using the PushToken as a Bearer token see [RFC 6750](https://tools.ietf.org/html/rfc6750) 127 | -------------------------------------------------------------------------------- /transports/mobile.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Mobile App or Browser to Uport Mobile Request Flow 4 | participant ClientApp 5 | participant UportMobile 6 | actor Owner 7 | 8 | ClientApp -> UportMobile : Open URL containing request 9 | UportMobile -> Owner : Authorize Request? 10 | Owner -> UportMobile: Allow/Disallow 11 | UportMobile -> ClientApp: Open URL containg callback url and Response 12 | 13 | @enduml 14 | -------------------------------------------------------------------------------- /transports/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/transports/mobile.png -------------------------------------------------------------------------------- /transports/mobile.svg: -------------------------------------------------------------------------------- 1 | Mobile App or Browser to Uport Mobile Request FlowClientAppClientAppUportMobileUportMobileOwnerOwnerOpen URL containing requestAuthorize Request?Allow/DisallowOpen URL containg callback url and Response -------------------------------------------------------------------------------- /transports/push.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Push Notification Transport" 3 | category: "transports" 4 | type: "reference" 5 | source: "https://github.com/uport-project/specs/blob/develop/transports/push.md" 6 | --- 7 | 8 | # Push Notification Transport 9 | Push notifications is a transport for sending requests to users. To make sure that the push notification service does not learn any information about what is in the request itself, all requests have to be encrypted. This means that sending a request as a push notification has two steps; encryption and a REST call. 10 | 11 | ### Proper encoding of the request 12 | 13 | The message SHOULD be encrypted. See the [message encryption](/messages/encryption.md) document for more details. 14 | 15 | First, simply wrap the request url in a JSON object and encode it as a string, like so: `{"url":""}`. 16 | We recommend padding the message so that it is less vulnerable to analysis attacks. This is done by appending spaces to the message until it is of length `N * I`, where `N` is any integer and `I` is some consistent number. We recommend `I = 50`. 17 | 18 | Lastly by decoding this UTF-8 string to bytes we get message `m`. 19 | 20 | ## Sending the request 21 | To send the now encrypted message the server described below is used. 22 | 23 | ### Push notification server 24 | 25 | The uPort push notification service is operating from the following url: 26 | 27 | `https://api.uport.me/pututu` 28 | 29 | It allows you to send encrypted push notifications to your user given that you have a `notification token` that you get when requesting the `'notifications'` permission in the [Selective Disclosure Flow](/flows/selectivedisclosure.md). 30 | 31 | #### Endpoint 32 | 33 | `POST /sns` 34 | 35 | #### Headers 36 | 37 | `Authorization: Bearer ` 38 | 39 | #### Body 40 | ``` 41 | { 42 | message: 43 | } 44 | ``` 45 | 46 | #### Response 47 | 48 | | Status | Message | | 49 | |:------:|----------------|-------------------------------| 50 | | 200 | Ok | Message Send | 51 | | 400 | Fail | endpointArn not supported | 52 | | 400 | Fail | token not signed by endpointArn user | 53 | | 403 | Forbidden | JWT token missing or invalid | 54 | | 500 | Internal Error | Internal error | 55 | 56 | #### Response data 57 | ``` 58 | { 59 | status: 'success', 60 | message: 61 | } 62 | ``` 63 | -------------------------------------------------------------------------------- /transports/push.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | !include ../uportskin.plantuml 3 | title Uport Push Notification Architecture v2 4 | actor UportMobile 5 | participant "Apple Push Server" as APNS 6 | participant AmazonSNS 7 | box "Uport Servicess" 8 | participant UportPushServer 9 | database Inbox 10 | end box 11 | participant ClientApp 12 | 13 | group Mobile Registration 14 | UportMobile <- APNS : Device Token 15 | UportMobile -> AmazonSNS: Device Token 16 | AmazonSNS -> UportMobile: Endpoint ARN 17 | end 18 | 19 | group App Request Permission 20 | ClientApp -> UportMobile : Selective Disclosure Request Token 21 | UportMobile -> ClientApp: Push Token (containing Endpoint ARN) 22 | end 23 | 24 | group Send Request 25 | ClientApp -> UportPushServer: PushToken and Encrypted Request 26 | UportPushServer -> Inbox: Store Encrypted Request and return message id 27 | UportPushServer -> UportPushServer: Extract Endpoint ARN from Push Token 28 | UportPushServer -> AmazonSNS: Push message id and EndpointARN 29 | AmazonSNS -> APNS: Message ID and Device Token 30 | APNS -> UportMobile : Message ID 31 | UportMobile <-> Inbox : Fetch Message 32 | UportMobile -> UportMobile : Decrypt Message 33 | end 34 | @enduml 35 | -------------------------------------------------------------------------------- /transports/push.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uport-project/specs/52cbf66c0ced19a40e96b4555e97d6e332cf0506/transports/push.png -------------------------------------------------------------------------------- /uportskin.plantuml: -------------------------------------------------------------------------------- 1 | skinparam roundcorner 10 2 | skinparam BoxPadding 10 3 | skinparam headerFontColor #6959DB 4 | skinparam headerFontSize 18 5 | skinparam sequence { 6 | ArrowColor #6959DB 7 | GroupBackgroundColor #6959DB 8 | GroupBorderColor #EDECFF 9 | GroupHeaderFontColor #ffffff 10 | BoxBorderColor #6959DB 11 | BoxBackgroundColor #EDECFF 12 | BoxPadding 10 13 | 14 | ParticipantBorderColor #6959DB 15 | ParticipantBackgroundColor #ffffff 16 | ParticipantPadding 10 17 | 18 | ActorBorderColor #6959DB 19 | ActorBackgroundColor #ffffff 20 | EntityBorderColor #6959DB 21 | EntityBackgroundColor #ffffff 22 | DatabaseBorderColor #6959DB 23 | DatabaseBackgroundColor #ffffff 24 | 25 | LifeLineBorderColor #878787 26 | } 27 | 28 | --------------------------------------------------------------------------------