diff --git a/README.md b/README.md
index 2427f6dd..dac956d5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# vcverifier
+# VCVerifier
-vcverifier includes in a single binary demo versions of Issuer, Verifier and Wallet (same-device only, for a cross-device wallet please see [VCWallet](https://github.com/hesusruiz/VCWallet)).
+VCerifier includes in a single binary demo versions of Issuer, Verifier and Wallet (same-device only, for a cross-device wallet please see [VCWallet](https://github.com/hesusruiz/VCWallet)).
This facilitates installation and allows to see how all components fit together and the protocol flows between them.
diff --git a/api/api.yaml b/api/api.yaml
index a228c430..ea12f262 100644
--- a/api/api.yaml
+++ b/api/api.yaml
@@ -10,133 +10,7 @@ tags:
description: All api-endpoints, e.g. the once that can be reused by other applicatios
- name: verifier
description: All endpoints belonging to the verifier functionality
- - name: issuer
- description: All endpoints belonging to the issuer functionality
- - name: core
- description: All endpoints belonging to core functionality, e.g. setup and info stuff
- - name: wallet
- description: All endpoints belonging to the wallet functionality
- - name: base
- description: General endpoints, providing information or entrypoint routing
paths:
- /issuer/api/v1/newcredential:
- get:
- tags:
- - issuer
- - frontend
- operationId: IssuerPageNewCredentialFormDisplay
- summary: Returns the page to issue new credentials
- responses:
- '200':
- description: Show the credentials input page
- content:
- text/html:
- schema:
- type: object
- post:
- tags:
- - issuer
- - frontend
- operationId: IssuerPageNewCredentialFormPost
- summary: Issues a new credentials and renders them to a page
- requestBody:
- required: true
- content:
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/VCCreate'
- responses:
- '200':
- description: Show the created credential on a rendered page
- content:
- text/html:
- schema:
- type: object
- /issuer/api/v1/creddetails/{id}:
- get:
- tags:
- - issuer
- - frontend
- parameters:
- - $ref: '#/components/parameters/Id'
- operationId: IssuerPageCredentialDetails
- summary: Show the credential details on a rendered page
- responses:
- '200':
- description: Present the credential details in a rendered html page
- content:
- text/html:
- schema:
- type: object
- /issuer/api/v1/displayqrurl/{id}:
- get:
- tags:
- - issuer
- - frontend
- parameters:
- - $ref: '#/components/parameters/Id'
- operationId: IssuerPageDisplayQRURL
- summary: Show the credential encoded as a qr-code on a rendered html page
- responses:
- '200':
- description: Present the credential qr-code in a rendered html page
- content:
- text/html:
- schema:
- type: object
-
- /issuer/api/v1/allcredentials:
- get:
- tags:
- - issuer
- - api
- operationId: IssuerAPIAllCredentials
- summary: Return the ids of all issued credentials
- responses:
- '200':
- description: Returns the list of ids
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/CredentialId'
- /issuer/api/v1/credential:
- post:
- tags:
- - issuer
- - api
- operationId: CreateNewCredential
- summary: Issues a new credentials and returns it
- requestBody:
- required: true
- content:
- application/x-www-form-urlencoded:
- schema:
- $ref: '#/components/schemas/VCCreate'
- responses:
- '200':
- description: Return the created credential
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/VerifiableCredential'
- /issuer/api/v1/credential/{id}:
- get:
- tags:
- - issuer
- - api
- parameters:
- - $ref: '#/components/parameters/Id'
- operationId: IssuerAPICredential
- summary: Return credential with the given ID
- responses:
- '200':
- description: The credential
- content:
- text/plain:
- schema:
- $ref: '#/components/schemas/VerifiableCredential'
/verifier/api/v1/displayqr:
get:
tags:
@@ -301,87 +175,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/JWKSet'
-
- /wallet/api/v1/selectcredential:
- get:
- tags:
- - wallet
- - frontend
- operationId: WalletPageSelectCredential
- summary: Present a rendered page with the available credentials
- responses:
- '200':
- description: The rendered page
- content:
- text/html:
- schema:
- type: object
- /wallet/api/v1/sendcredential:
- get:
- tags:
- - wallet
- - frontend
- parameters:
- - $ref: '#/components/parameters/QueryState'
- - $ref: '#/components/parameters/QueryId'
- - $ref: '#/components/parameters/RedirectUri'
- operationId: WalletPageSendCredential
- summary: Executes a post with the given credential to the redirect_uri and returns a rendered page, presenting the post-result
- responses:
- '200':
- description: The rendered page
- content:
- text/html:
- schema:
- type: object
- /core/api/v1/createdid:
- get:
- tags:
- - core
- - api
- operationId: CoreAPICreateDID
- summary: Wrapper for the waltid-ssikit custodian endpoint for creating did's of type key
- responses:
- '200':
- description: Creation result from the ssikit
- content:
- application/json:
- schema:
- type: string
- example: did:key:z6MkibsKombswvCMhej83YbHsyknL2uc5kVazwr4xB3JqeUX
- /core/api/v1/listcredentialtemplates:
- get:
- tags:
- - core
- - api
- operationId: CoreAPIListCredentialTemplates
- summary: Wrapper for the waltid-ssikit signatory endpoint for listing templates
- responses:
- '200':
- description: List of templates, no offical spec available from ssikit, format is assumed
- content:
- application/json:
- schema:
- type: array
- items:
- $ref: '#/components/schemas/CredentialTemplate'
- /core/api/v1/getcredentialtemplate/{id}:
- get:
- tags:
- - core
- - api
- parameters:
- - $ref: '#/components/parameters/Id'
- operationId: CoreAPIGetCredentialTemplate
- summary: Wrapper for the waltid-ssikit signatory endpoint for retrieving a template
- responses:
- '200':
- description: The requested template.
- content:
- application/json:
- schema:
- description: SSIKit doc says type string, but its a vc-object instead.
- type: object
/:
get:
tags:
@@ -396,20 +189,6 @@ paths:
text/plain:
schema:
type: object
- /issuer:
- get:
- tags:
- - base
- - frontend
- operationId: HandleIssuerHome
- summary: Returns the rendered homepage for the issuer functionality
- responses:
- '200':
- description: The issuer page
- content:
- text/plain:
- schema:
- type: object
/verifier:
get:
tags:
@@ -477,23 +256,6 @@ components:
type: string
example: https://my-verifier.com/verifier/api/v1/authenticationresponse
schemas:
- CredentialTemplate:
- type: object
- properties:
- mutable:
- type: boolean
- example: false
- name:
- type: string
- example: VerifiableId
- template:
- type: string
- CredentialId:
- type: object
- properties:
- id:
- type: string
- example: urn:uuid:e44beccc-a766-4baf-8cd0-17f86ef74a5c
CredentialsType:
type: array
items:
@@ -608,25 +370,6 @@ components:
properties:
credential:
$ref: '#/components/schemas/VerifiableCredential'
- VCCreate:
- type: object
- properties:
- email:
- type: string
- format: email
- example: test@test.org
- firstName:
- type: string
- example: Test
- familyName:
- type: string
- example: User
- target:
- type: string
- example: did:key:myuserkey
- roles:
- type: string
- example: GOLD_CUSTOMER,STANDARD_CUSTOMER
BackendInfo:
type: object
properties:
diff --git a/back/operations/ssikit.go b/back/operations/ssikit.go
index 5745a9aa..9a7c1049 100644
--- a/back/operations/ssikit.go
+++ b/back/operations/ssikit.go
@@ -1,24 +1,33 @@
package operations
import (
+ "encoding/json"
"errors"
"fmt"
"net/http"
+ model "github.com/fiware/vcverifier/model"
"github.com/fiware/vcverifier/vault"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
+const verificationPath = "/v1/verify"
+
var logger = zap.Must(zap.NewDevelopment())
-func SSIKitCreateDID(custodianURL string, v *vault.Vault, userid string) (string, error) {
+func SSIKitCreateDID(custodianURL string, v *vault.Vault, userid string, configueredDid string) (did string, err error) {
defer logger.Sync()
- // Create a new DID only if it does not exist
- did, _ := v.GetDIDForUser(userid)
- if len(did) > 0 {
- return did, nil
+ if len(configueredDid) == 0 {
+ // Create a new DID only if it does not exist
+ did, err = v.GetDIDForUser(userid)
+ if len(did) > 0 {
+ return did, err
+ }
+ } else {
+ v.SetDIDForUser(userid, configueredDid)
+ return did, err
}
// Call the SSI Kit
@@ -31,7 +40,7 @@ func SSIKitCreateDID(custodianURL string, v *vault.Vault, userid string) (string
agent.Set("accept", "application/json")
code, returnBody, reqErr := agent.Bytes()
if len(reqErr) > 0 {
- err := fmt.Errorf("error calling SSI Kit: %v", reqErr[0])
+ err := fmt.Errorf("error calling SSI Kit at: %v", reqErr[0])
logger.Error("error calling SSI Kit", zap.Error(err))
return "", err
}
@@ -46,3 +55,64 @@ func SSIKitCreateDID(custodianURL string, v *vault.Vault, userid string) (string
return did, nil
}
+
+type verificationRequest struct {
+ Policies []model.Policy `json:"policies"`
+ Credentials []map[string]interface{} `json:"credentials"`
+}
+
+type verificationResult struct {
+ Valid bool `json:"valid"`
+ PolicyResults map[string]bool `json:"policyResults`
+}
+
+type verificationResponse struct {
+ Valid bool `json:"valid"`
+ Results []verificationResult `json:"results"`
+}
+
+func VerifyVC(auditorURL string, policies []model.Policy, verifiableCredential map[string]interface{}) (result bool, err error) {
+ defer logger.Sync()
+ auditorAddress := auditorURL + verificationPath
+ // Call the SSI Kit
+ agent := fiber.Post(auditorURL + verificationPath)
+ verificationRequest := verificationRequest{policies, []map[string]interface{}{verifiableCredential}}
+
+ agent.JSON(verificationRequest)
+
+ logger.Debug(fmt.Sprintf("Request is %s", prettyPrintObject(verificationRequest)))
+ agent.ContentType("application/json")
+ agent.Set("accept", "application/json")
+
+ code, returnBody, reqErr := agent.Bytes()
+ if len(reqErr) > 0 {
+ err := fmt.Errorf("error calling SSI Kit at %s: %v", auditorAddress, reqErr[0])
+ logger.Error("error calling SSI Kit", zap.Error(err))
+ return false, err
+ }
+ if code != http.StatusOK {
+ err := fmt.Errorf("error calling SSI Kit - status was %d: %s", code, string(returnBody))
+ logger.Error(fmt.Sprintf("error calling SSI Kit - response was %d: %s", code, string(returnBody)), zap.Error(err))
+ return false, err
+ }
+
+ var vr verificationResponse
+ json.Unmarshal(returnBody, &vr)
+
+ if vr.Valid {
+ return true, err
+ } else {
+ logger.Info("Verfication failed.")
+ logger.Debug(fmt.Sprintf("Detailed result is %v", prettyPrintObject(vr)))
+ return false, err
+ }
+}
+
+func prettyPrintObject(objectInterface interface{}) string {
+ jsonBytes, err := json.Marshal(objectInterface)
+ if err != nil {
+ logger.Debug(fmt.Sprintf("Was not able to pretty print the object: %v", objectInterface))
+ return ""
+ }
+ return string(jsonBytes)
+}
diff --git a/back/views/layouts/main.html b/back/views/layouts/main.html
index 0593fbf0..c6d1ec26 100644
--- a/back/views/layouts/main.html
+++ b/back/views/layouts/main.html
@@ -9,7 +9,7 @@
-
Credential Issuer
+ Credential Verifier
diff --git a/back/views/partials/header.html b/back/views/partials/header.html
index b194ebc5..f75bd2e5 100644
--- a/back/views/partials/header.html
+++ b/back/views/partials/header.html
@@ -2,4 +2,6 @@
FIWARE Verifiable Credentials
+
+
diff --git a/back/views/verifier_loginexpired.html b/back/views/verifier_loginexpired.html
index cf7aadb2..854df89f 100644
--- a/back/views/verifier_loginexpired.html
+++ b/back/views/verifier_loginexpired.html
@@ -10,7 +10,7 @@ The login session has expired
Click the button to try again
-
Home
+
Home
diff --git a/back/views/verifier_present_qr.html b/back/views/verifier_present_qr.html
index 822d0c8e..937e9b5e 100644
--- a/back/views/verifier_present_qr.html
+++ b/back/views/verifier_present_qr.html
@@ -27,7 +27,7 @@ Or click this button to authenticate with the browser
}
const data = await response.text()
if (data === "expired") {
- location = "{{.verifierPrefix}}/loginexpired"
+ location = "{{.frontendPrefix}}/loginexpired"
return
}
if (data === "pending") {
@@ -35,7 +35,7 @@ Or click this button to authenticate with the browser
}
console.log("Received:", data)
- location = "{{.verifierPrefix}}/receivecredential/{{.state}}"
+ location = "{{.frontendPrefix}}/receivecredential/{{.state}}"
return
} catch (error) {
window.alert("A server error happened.")
diff --git a/back/views/verifier_receivedcredential.html b/back/views/verifier_receivedcredential.html
index 1be1a375..d5c8649c 100644
--- a/back/views/verifier_receivedcredential.html
+++ b/back/views/verifier_receivedcredential.html
@@ -12,7 +12,7 @@ Received credential:
diff --git a/back/views/verifier_request_service.html b/back/views/verifier_request_service.html
new file mode 100644
index 00000000..13b3253f
--- /dev/null
+++ b/back/views/verifier_request_service.html
@@ -0,0 +1,36 @@
+{{define "verifier_request_service"}} {{template "partials/header" .}}
+
+
+
+
+
Enter URL:
+
+
+
+
+ {{if .returnBody}}
+
+
Response
+
With status code {{.code}} and returned:
+
{{.returnBody}}
+
+ {{end}}
+
+
+
+{{template "partials/footer" .}} {{end}}
\ No newline at end of file
diff --git a/back/www/img/logo.png b/back/www/img/logo.png
new file mode 100644
index 00000000..88974aee
Binary files /dev/null and b/back/www/img/logo.png differ
diff --git a/configs/server.yaml b/configs/server.yaml
index d59da7e5..857ddc1c 100644
--- a/configs/server.yaml
+++ b/configs/server.yaml
@@ -11,6 +11,7 @@ store:
verifier:
id: PacketDelivery
+ did: did:key:z6Mkv4Lh9zBTPLoFhLHHMFJA7YAeVw5HFYZV8rkdfY9fNtm3
name: PacketDelivery
password: ThePassword
store:
@@ -18,6 +19,7 @@ verifier:
dataSourceName: "file:verifier.sqlite?mode=rwc&cache=shared&_fk=1"
uri_prefix: /verifier
jwks_uri: /.well-known/jwks_uri
+ tirAddress: "https://api-pilot.ebsi.eu/trusted-issuers-registry/v2/issuers/"
protectedResource:
url: "https://www.google.com"
diff --git a/frontend.go b/frontend.go
new file mode 100644
index 00000000..51b3ef2c
--- /dev/null
+++ b/frontend.go
@@ -0,0 +1,241 @@
+package main
+
+import (
+ "encoding/base64"
+ "fmt"
+ "time"
+
+ "github.com/gofiber/fiber/v2"
+ qrcode "github.com/skip2/go-qrcode"
+ "go.uber.org/zap"
+)
+
+const frontendPrefix = ""
+
+type Frontend struct {
+ server *Server
+}
+
+// setupVerifier creates and setups the Issuer routes
+func setupFrontend(s *Server) {
+
+ frontend := &Frontend{s}
+
+ // Define the prefix for Verifier routes
+ frontendRoutes := s.Group(frontendPrefix)
+
+ // Pages
+ frontendRoutes.Get("/", frontend.HandleVerifierHome)
+ // Display a QR code for mobile wallet or a link for enterprise wallet
+ frontendRoutes.Get("/displayqr", frontend.VerifierPageDisplayQRSIOP)
+
+ // Error page when login session has expired without the user sending the credential
+ frontendRoutes.Get("/loginexpired", frontend.VerifierPageLoginExpired)
+
+ // Page displaying the received credential, after successful login
+ frontendRoutes.Get("/receivecredential/:state", frontend.VerifierPageReceiveCredential)
+
+ // Allow simulation of accessing protected resources, after successful login
+ frontendRoutes.Get("/accessprotectedservice", frontend.VerifierPageAccessProtectedService)
+ frontendRoutes.Post("/accessService", frontend.VerifierPageAccessServicePost)
+ frontendRoutes.Get("/accessService", frontend.VerifierPageAccessServiceGet)
+}
+
+func (f *Frontend) HandleVerifierHome(c *fiber.Ctx) error {
+
+ // Get the list of credentials
+ credsSummary, err := f.server.Operations.GetAllCredentials()
+ if err != nil {
+ return err
+ }
+
+ // Render template
+ m := fiber.Map{
+ "verifierPrefix": frontendPrefix,
+ "prefix": frontendPrefix,
+ "credlist": credsSummary,
+ }
+ return c.Render("verifier_home", m)
+}
+
+func (f *Frontend) VerifierPageDisplayQRSIOP(c *fiber.Ctx) error {
+
+ // Generate the state that will be used for checking expiration and also successful logon
+ state := generateNonce()
+
+ // Create an entry in storage that will expire.
+ // The entry is identified by the nonce
+ f.server.storage.Set(state, []byte("pending"), 200*time.Second)
+
+ // This is the endpoint inside the QR that the wallet will use to send the VC/VP
+ redirect_uri := c.Protocol() + "://" + c.Hostname() + verifierPrefix + "/authenticationresponse"
+
+ // Create the Authentication Request
+ authRequest := createAuthenticationRequest(f.server.verifierDID, redirect_uri, state)
+ f.server.logger.Info("AuthRequest", authRequest)
+
+ // Create the QR code for cross-device SIOP
+ png, err := qrcode.Encode(authRequest, qrcode.Medium, 256)
+ if err != nil {
+ return err
+ }
+
+ // Convert the image data to a dataURL
+ base64Img := base64.StdEncoding.EncodeToString(png)
+ base64Img = "data:image/png;base64," + base64Img
+
+ // Render the page
+ m := fiber.Map{
+ "verifierPrefix": verifierPrefix,
+ "frontendPrefix": frontendPrefix,
+ "qrcode": base64Img,
+ "prefix": frontendPrefix,
+ "state": state,
+ }
+ return c.Render("verifier_present_qr", m)
+}
+
+func (f *Frontend) VerifierPageLoginExpired(c *fiber.Ctx) error {
+ m := fiber.Map{
+ "prefix": frontendPrefix,
+ }
+ return c.Render("verifier_loginexpired", m)
+}
+
+func (f *Frontend) VerifierPageReceiveCredential(c *fiber.Ctx) error {
+
+ f.server.logger.Infof("Received credential")
+
+ // Get the state as a path parameter
+ state := c.Params("state")
+
+ // get the credential from the storage
+ rawCred, _ := f.server.storage.Get(state)
+ if len(rawCred) == 0 {
+ // Render an error
+ m := fiber.Map{
+ "error": "No credential found",
+ "frontendPrefix": frontendPrefix,
+ }
+ return c.Render("displayerror", m)
+ }
+
+ claims := string(rawCred)
+
+ // Create an access token from the credential
+ accessToken, err := f.server.verifierVault.CreateAccessToken(claims, f.server.cfg.String("verifier.id"))
+ if err != nil {
+ return err
+ }
+
+ // Set it in a cookie
+ cookie := new(fiber.Cookie)
+ cookie.Name = "dbsamvf"
+ cookie.Value = string(accessToken)
+ cookie.Expires = time.Now().Add(1 * time.Hour)
+
+ // Set cookie
+ c.Cookie(cookie)
+
+ // Set also the access token in the Authorization field of the response header
+ bearer := "Bearer " + string(accessToken)
+ c.Set("Authorization", bearer)
+
+ // Render
+ m := fiber.Map{
+ "verifierPrefix": frontendPrefix,
+ "frontendPrefix": frontendPrefix,
+ "claims": claims,
+ "prefix": frontendPrefix,
+ }
+ return c.Render("verifier_receivedcredential", m)
+}
+
+func (f *Frontend) VerifierPageAccessServiceGet(c *fiber.Ctx) error {
+
+ protected := f.server.cfg.String("verifier.protectedResource.url")
+ // Render
+ m := fiber.Map{
+ "protectedService": protected,
+ "frontendPrefix": frontendPrefix,
+ "verifierPrefix": verifierPrefix,
+ }
+ return c.Render("verifier_request_service", m)
+}
+
+func (f *Frontend) VerifierPageAccessServicePost(c *fiber.Ctx) error {
+ var code int
+ var returnBody []byte
+ var errors []error
+
+ // Get the access token from the cookie
+ accessToken := c.Cookies("dbsamvf")
+ service := &AccessServiceForm{}
+ if err := c.BodyParser(service); err != nil {
+ f.server.logger.Infof("Error parsing: %s", err)
+ return err
+ }
+ f.server.logger.Errorw(string(c.Body()))
+ // Prepare to GET to the url
+ agent := fiber.Get(service.Url)
+
+ // Set the Authentication header
+ agent.Set("Authorization", "Bearer "+accessToken)
+
+ agent.Set("accept", "application/json")
+ code, returnBody, errors = agent.Bytes()
+ if len(errors) > 0 {
+ f.server.logger.Errorw("error calling backend at "+service.Url, zap.Errors("errors", errors))
+ return fmt.Errorf("error calling backend as %s: %v", service.Url, errors[0])
+ }
+
+ // Render
+ m := fiber.Map{
+ "protectedService": service.Url,
+ "verifierPrefix": frontendPrefix,
+ "frontendPrefix": frontendPrefix,
+ "code": code,
+ "returnBody": string(returnBody),
+ }
+ return c.Render("verifier_request_service", m)
+}
+
+func (f *Frontend) VerifierPageAccessProtectedService(c *fiber.Ctx) error {
+
+ var code int
+ var returnBody []byte
+ var errors []error
+
+ // Get the access token from the cookie
+ accessToken := c.Cookies("dbsamvf")
+
+ // Check if the user has configured a protected service to access
+ protected := f.server.cfg.String("verifier.protectedResource.url")
+ if len(protected) > 0 {
+
+ // Prepare to GET to the url
+ agent := fiber.Get(protected)
+
+ // Set the Authentication header
+ agent.Set("Authorization", "Bearer "+accessToken)
+
+ agent.Set("accept", "application/json")
+ code, returnBody, errors = agent.Bytes()
+ if len(errors) > 0 {
+ f.server.logger.Errorw("error calling SSI Kit", zap.Errors("errors", errors))
+ return fmt.Errorf("error calling SSI Kit: %v", errors[0])
+ }
+
+ }
+
+ // Render
+ m := fiber.Map{
+ "verifierPrefix": frontendPrefix,
+ "accesstoken": accessToken,
+ "frontendPrefix": frontendPrefix,
+ "protected": protected,
+ "code": code,
+ "returnBody": string(returnBody),
+ }
+ return c.Render("verifier_protectedservice", m)
+}
diff --git a/go.mod b/go.mod
index 2ab9e924..ce8c77a8 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,9 @@ require (
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
+ github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
+ github.com/deepmap/oapi-codegen v1.12.3 // indirect
github.com/goccy/go-yaml v1.9.6 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.13 // indirect
@@ -43,7 +45,7 @@ require (
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/gofiber/template v1.7.2
- github.com/golang-jwt/jwt/v4 v4.4.2
+ github.com/golang-jwt/jwt/v4 v4.4.3
github.com/google/certificate-transparency-go v1.1.4 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0
diff --git a/go.sum b/go.sum
index 2f1b1b31..a1b22c8d 100644
--- a/go.sum
+++ b/go.sum
@@ -95,6 +95,7 @@ github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuN
github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@@ -116,6 +117,8 @@ github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g
github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
+github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
@@ -149,6 +152,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
+github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
@@ -216,6 +220,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
+github.com/deepmap/oapi-codegen v1.12.3 h1:+DDYKeIwlKChzHjhVtlISegatFevDDazBhtk/dnp4V4=
+github.com/deepmap/oapi-codegen v1.12.3/go.mod h1:ao2aFwsl/muMHbez870+KelJ1yusV01RznwAFFrVjDc=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@@ -250,6 +256,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
+github.com/fiware/dsba-pdp v0.0.0-20230111112802-540544166240 h1:8YP/LLW4jrDR20YJFirqn+XO7wZdVJlGZBAipQMqOH4=
+github.com/fiware/dsba-pdp v0.0.0-20230111112802-540544166240/go.mod h1:HpYuQF4RWRFxAFkqItyaCszNbOarnCOFuqKxWMGvFEI=
github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
@@ -316,6 +324,8 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
+github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -532,6 +542,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
@@ -812,6 +823,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
+github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
diff --git a/main.go b/main.go
index c338296d..877af66b 100644
--- a/main.go
+++ b/main.go
@@ -17,7 +17,6 @@ import (
"github.com/hesusruiz/vcutils/yaml"
"flag"
- "log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
@@ -34,15 +33,11 @@ const defaultStoreDriverName = "sqlite3"
const defaultStoreDataSourceName = "file:issuer.sqlite?mode=rwc&cache=shared&_fk=1"
const defaultPassword = "ThePassword"
-const corePrefix = "/core/api/v1"
-const issuerPrefix = "/issuer/api/v1"
const verifierPrefix = "/verifier/api/v1"
-const walletPrefix = "/wallet/api/v1"
var (
prod = flag.Bool("prod", false, "Enable prefork in Production")
configFile = flag.String("config", LookupEnvOrString("CONFIG_FILE", defaultConfigFile), "path to configuration file")
- password = flag.String("pass", LookupEnvOrString("PASSWORD", defaultPassword), "admin password for the server")
)
type SSIKitConfig struct {
@@ -60,7 +55,6 @@ type Server struct {
WebAuthn *handlers.WebAuthnHandler
Operations *operations.Manager
verifierVault *vault.Vault
- issuerDID string
verifierDID string
logger *zap.SugaredLogger
storage *memory.Storage
@@ -130,7 +124,7 @@ func BackendServer() {
s.logger.Infof("SSIKit is configured at: %v", s.ssiKit)
- s.verifierDID, err = operations.SSIKitCreateDID(s.ssiKit.custodianUrl, s.verifierVault, cfg.String("verifier.id"))
+ s.verifierDID, err = operations.SSIKitCreateDID(s.ssiKit.custodianUrl, s.verifierVault, cfg.String("verifier.id"), s.cfg.String("verifier.did"))
if err != nil {
panic(err)
}
@@ -149,11 +143,6 @@ func BackendServer() {
s.storage = memory.New()
defer s.storage.Close()
- // ##########################
- // Application Home pages
- s.Get("/", s.HandleHome)
- s.Get("/verifier", s.HandleVerifierHome)
-
// Info base path
s.Get("/info", s.GetBackendInfo)
@@ -163,28 +152,17 @@ func BackendServer() {
}
setupVerifier(s)
- setupCoreRoutes(s)
+ setupFrontend(s)
// Setup static files
s.Static("/static", cfg.String("server.staticDir", defaultStaticDir))
+ s.logger.Infof("Start %s", cfg.String("server.listenAddress"))
// Start the server
- log.Fatal(s.Listen(cfg.String("server.listenAddress")))
-
-}
-
-func setupCoreRoutes(s *Server) {
- // ########################################
- // Core routes
- coreRoutes := s.Group(corePrefix)
-
- // Create DID
- coreRoutes.Get("/createdid", s.CoreAPICreateDID)
- // List Templates
- coreRoutes.Get("/listcredentialtemplates", s.CoreAPIListCredentialTemplates)
- // Get one template
- coreRoutes.Get("/getcredentialtemplate/:id", s.CoreAPIGetCredentialTemplate)
-
+ err = s.Listen(cfg.String("server.listenAddress"))
+ if err != nil {
+ s.logger.Warn("Failed to start.", err)
+ }
}
func fromMap(configMap map[string]any) (skc *SSIKitConfig) {
@@ -232,23 +210,6 @@ func (s *Server) HandleStop(c *fiber.Ctx) error {
return nil
}
-func (s *Server) HandleVerifierHome(c *fiber.Ctx) error {
-
- // Get the list of credentials
- credsSummary, err := s.Operations.GetAllCredentials()
- if err != nil {
- return err
- }
-
- // Render template
- m := fiber.Map{
- "verifierPrefix": verifierPrefix,
- "prefix": verifierPrefix,
- "credlist": credsSummary,
- }
- return c.Render("verifier_home", m)
-}
-
func generateNonce() string {
b := make([]byte, 16)
io.ReadFull(rand.Reader, b)
@@ -256,8 +217,6 @@ func generateNonce() string {
return nonce
}
-var sameDevice = false
-
type jwkSet struct {
Keys []*jwk.JWK `json:"keys"`
}
diff --git a/model/verifiablecredential.go b/model/verifiablecredential.go
new file mode 100644
index 00000000..37585875
--- /dev/null
+++ b/model/verifiablecredential.go
@@ -0,0 +1,10 @@
+package model
+
+type Policy struct {
+ Policy string `json:"policy"`
+ Argument *TirArgument `json:"argument,omitempty"`
+}
+
+type TirArgument struct {
+ RegistryAddress string `json:"registryAddress"`
+}
diff --git a/verifier.go b/verifier.go
index 98491a99..c41317b7 100644
--- a/verifier.go
+++ b/verifier.go
@@ -4,9 +4,11 @@ import (
"encoding/base64"
"encoding/json"
"errors"
- "fmt"
+ "net/http"
"time"
+ "github.com/fiware/vcverifier/back/operations"
+ model "github.com/fiware/vcverifier/model"
"github.com/gofiber/fiber/v2"
"github.com/hesusruiz/vcutils/yaml"
qrcode "github.com/skip2/go-qrcode"
@@ -15,13 +17,26 @@ import (
)
type Verifier struct {
- server *Server
+ server *Server
+ tirAddress *string
}
// setupVerifier creates and setups the Issuer routes
func setupVerifier(s *Server) {
- verifier := &Verifier{s}
+ configuredAddress := s.cfg.String("verifier.tirAddress")
+
+ var tirAddress *string
+
+ if configuredAddress == "" {
+ s.logger.Warn("No trusted issuer registry configured. Will not use the tir check")
+ tirAddress = nil
+ } else {
+ s.logger.Infof("Will use tir at %s to verify the credentials.", configuredAddress)
+ tirAddress = &configuredAddress
+ }
+
+ verifier := &Verifier{s, tirAddress}
// Define the prefix for Verifier routes
verifierRoutes := s.Group(verifierPrefix)
@@ -32,23 +47,6 @@ func setupVerifier(s *Server) {
jwks_uri := s.cfg.String("verifier.uri_prefix") + s.cfg.String("verifier.jwks_uri")
s.Get(jwks_uri, s.VerifierAPIJWKS)
- // Pages
-
- // Display a QR code for mobile wallet or a link for enterprise wallet
- verifierRoutes.Get("/displayqr", verifier.VerifierPageDisplayQRSIOP)
-
- // Error page when login session has expired without the user sending the credential
- verifierRoutes.Get("/loginexpired", verifier.VerifierPageLoginExpired)
-
- // For same-device logins (e.g., with the enterprise wallet)
- verifierRoutes.Get("/startsiopsamedevice", verifier.VerifierPageStartSIOPSameDevice)
-
- // Page displaying the received credential, after successful login
- verifierRoutes.Get("/receivecredential/:state", verifier.VerifierPageReceiveCredential)
-
- // Allow simulation of accessing protected resources, after successful login
- verifierRoutes.Get("/accessprotectedservice", verifier.VerifierPageAccessProtectedService)
-
// APIs
// Used by the login page from the browser, to check successful login or expiration
@@ -62,183 +60,17 @@ func setupVerifier(s *Server) {
// Used by the wallet (both enterprise and mobile) to send the VC/VP as Authentication Response
verifierRoutes.Post("/authenticationresponse", verifier.VerifierAPIAuthenticationResponse)
+ s.logger.Info("Routes are setup")
}
-func (v *Verifier) VerifierPageDisplayQRSIOP(c *fiber.Ctx) error {
-
- // Generate the state that will be used for checking expiration and also successful logon
- state := generateNonce()
-
- // Create an entry in storage that will expire.
- // The entry is identified by the nonce
- v.server.storage.Set(state, []byte("pending"), 200*time.Second)
-
- // This is the endpoint inside the QR that the wallet will use to send the VC/VP
- redirect_uri := c.Protocol() + "://" + c.Hostname() + verifierPrefix + "/authenticationresponse"
-
- // Create the Authentication Request
- authRequest := createAuthenticationRequest(v.server.verifierDID, redirect_uri, state)
- v.server.logger.Info("AuthRequest", authRequest)
-
- // Create the QR code for cross-device SIOP
- png, err := qrcode.Encode(authRequest, qrcode.Medium, 256)
- if err != nil {
- return err
- }
-
- // Convert the image data to a dataURL
- base64Img := base64.StdEncoding.EncodeToString(png)
- base64Img = "data:image/png;base64," + base64Img
-
- // Render the page
- m := fiber.Map{
- "issuerPrefix": issuerPrefix,
- "verifierPrefix": verifierPrefix,
- "walletPrefix": walletPrefix,
- "qrcode": base64Img,
- "prefix": verifierPrefix,
- "state": state,
- }
- return c.Render("verifier_present_qr", m)
-}
-
-func (v *Verifier) VerifierPageLoginExpired(c *fiber.Ctx) error {
- m := fiber.Map{
- "prefix": verifierPrefix,
- }
- return c.Render("verifier_loginexpired", m)
-}
-
-func (v *Verifier) VerifierPageStartSIOPSameDevice(c *fiber.Ctx) error {
-
- state := c.Query("state")
-
- const scope = "dsba.credentials.presentation.PacketDeliveryService"
- const response_type = "vp_token"
- redirect_uri := c.Protocol() + "://" + c.Hostname() + verifierPrefix + "/authenticationresponse"
-
- // template := "https://hesusruiz.github.io/faster/?scope={{scope}}" +
- // "&response_type={{response_type}}" +
- // "&response_mode=post" +
- // "&client_id={{client_id}}" +
- // "&redirect_uri={{redirect_uri}}" +
- // "&state={{state}}" +
- // "&nonce={{nonce}}"
-
- walletUri := c.Protocol() + "://" + c.Hostname() + walletPrefix + "/selectcredential"
- template := walletUri + "/?scope={{scope}}" +
- "&response_type={{response_type}}" +
- "&response_mode=post" +
- "&client_id={{client_id}}" +
- "&redirect_uri={{redirect_uri}}" +
- "&state={{state}}" +
- "&nonce={{nonce}}"
-
- t := fasttemplate.New(template, "{{", "}}")
- str := t.ExecuteString(map[string]interface{}{
- "scope": scope,
- "response_type": response_type,
- "client_id": v.server.verifierDID,
- "redirect_uri": redirect_uri,
- "state": state,
- "nonce": generateNonce(),
- })
- fmt.Println(str)
-
- return c.Redirect(str)
-}
-
-func (v *Verifier) VerifierPageReceiveCredential(c *fiber.Ctx) error {
-
- // Get the state as a path parameter
- state := c.Params("state")
-
- // get the credential from the storage
- rawCred, _ := v.server.storage.Get(state)
- if len(rawCred) == 0 {
- // Render an error
- m := fiber.Map{
- "error": "No credential found",
- }
- return c.Render("displayerror", m)
- }
-
- claims := string(rawCred)
-
- // Create an access token from the credential
- accessToken, err := v.server.verifierVault.CreateAccessToken(claims, v.server.cfg.String("verifier.id"))
- if err != nil {
- return err
- }
-
- // Set it in a cookie
- cookie := new(fiber.Cookie)
- cookie.Name = "dbsamvf"
- cookie.Value = string(accessToken)
- cookie.Expires = time.Now().Add(1 * time.Hour)
-
- // Set cookie
- c.Cookie(cookie)
-
- // Set also the access token in the Authorization field of the response header
- bearer := "Bearer " + string(accessToken)
- c.Set("Authorization", bearer)
-
- // Render
- m := fiber.Map{
- "issuerPrefix": issuerPrefix,
- "verifierPrefix": verifierPrefix,
- "walletPrefix": walletPrefix,
- "claims": claims,
- "prefix": verifierPrefix,
- }
- return c.Render("verifier_receivedcredential", m)
-}
-
-func (v *Verifier) VerifierPageAccessProtectedService(c *fiber.Ctx) error {
-
- var code int
- var returnBody []byte
- var errors []error
-
- // Get the access token from the cookie
- accessToken := c.Cookies("dbsamvf")
-
- // Check if the user has configured a protected service to access
- protected := v.server.cfg.String("verifier.protectedResource.url")
- if len(protected) > 0 {
-
- // Prepare to GET to the url
- agent := fiber.Get(protected)
-
- // Set the Authentication header
- agent.Set("Authorization", "Bearer "+accessToken)
-
- agent.Set("accept", "application/json")
- code, returnBody, errors = agent.Bytes()
- if len(errors) > 0 {
- v.server.logger.Errorw("error calling SSI Kit", zap.Errors("errors", errors))
- return fmt.Errorf("error calling SSI Kit: %v", errors[0])
- }
-
- }
-
- // Render
- m := fiber.Map{
- "issuerPrefix": issuerPrefix,
- "verifierPrefix": verifierPrefix,
- "walletPrefix": walletPrefix,
- "accesstoken": accessToken,
- "protected": protected,
- "code": code,
- "returnBody": string(returnBody),
- }
- return c.Render("verifier_protectedservice", m)
+type AccessServiceForm struct {
+ Url string `form:"requestUrl,omitempty"`
}
func (v *Verifier) VerifierAPIPoll(c *fiber.Ctx) error {
+ v.server.logger.Debug("VerifierAPIPoll")
// get the state
state := c.Params("state")
@@ -254,7 +86,7 @@ func (v *Verifier) VerifierAPIPoll(c *fiber.Ctx) error {
// retrieve token for the given session("state"-paramter)
func (v *Verifier) VerifierAPIToken(c *fiber.Ctx) error {
- v.server.logger.Info("Get the token")
+ v.server.logger.Debug("Get the token")
// get the state
state := c.Params("state")
@@ -283,6 +115,8 @@ func (v *Verifier) VerifierAPIToken(c *fiber.Ctx) error {
func (v *Verifier) VerifierAPIStartSIOP(c *fiber.Ctx) error {
+ v.server.logger.Debug("Start siop")
+
// Get the state
state := c.Query("state")
@@ -311,16 +145,20 @@ func (v *Verifier) VerifierAPIStartSIOP(c *fiber.Ctx) error {
return c.SendString(str)
}
+type verficationMsg struct {
+ Msg string `json:"message"`
+}
+
// VerifierAPIAuthenticationResponseVP receives a VP, extracts the VC and display a page
func (v *Verifier) VerifierAPIAuthenticationResponseVP(c *fiber.Ctx) error {
+ v.server.logger.Debug("Received VP")
+
// Get the state, which indicates the login session to which this request belongs
state := c.Query("state")
// We should receive the Verifiable Presentation in the body as JSON
body := c.Body()
- fmt.Println(string(body))
- fmt.Println(string(state))
// Decode into a map
vp, err := yaml.ParseJson(string(body))
@@ -329,21 +167,54 @@ func (v *Verifier) VerifierAPIAuthenticationResponseVP(c *fiber.Ctx) error {
return err
}
- credential := vp.String("credential")
+ credential := []byte(vp.String("credential"))
// Validate the credential
+ res, err := v.verifyCredential(credential)
+ if err != nil {
+ v.server.logger.Errorw("Was not able to verify credential.", zap.Error(err))
+ return err
+ }
+ if !res {
+ v.server.logger.Info("Credential is not valid.")
+ return c.Status(http.StatusUnauthorized).JSON(verficationMsg{"Credential is invalid."})
+ }
// Set the credential in storage, and wait for the polling from client
- v.server.storage.Set(state, []byte(credential), 10*time.Second)
-
+ err = v.server.storage.Set(state, credential, 10*time.Second)
+ if err != nil {
+ v.server.logger.Warnf("Was not able to store for %s", state)
+ }
return c.SendString("ok")
}
-type verifiableCredential struct {
+func (v *Verifier) verifyCredential(credential []byte) (result bool, err error) {
+ v.server.logger.Debug("Verify Credential")
+
+ var vcToVerify map[string]interface{}
+
+ json.Unmarshal(credential, &vcToVerify)
+
+ policies := []model.Policy{
+ {Policy: "SignaturePolicy"},
+ {Policy: "IssuedDateBeforePolicy"},
+ {Policy: "ValidFromBeforePolicy"},
+ {Policy: "ExpirationDateAfterPolicy"},
+ }
+ if v.tirAddress != nil {
+ policies = append(policies, model.Policy{Policy: "TrustedIssuerRegistryPolicy", Argument: &model.TirArgument{RegistryAddress: *v.tirAddress}})
+ }
+
+ return operations.VerifyVC(v.server.ssiKit.auditorUrl, policies, vcToVerify)
+}
+
+type VerifiableCredential struct {
Credential *json.RawMessage `json:"credential"`
}
func (v *Verifier) VerifierAPIAuthenticationResponse(c *fiber.Ctx) error {
+ v.server.logger.Debug("Auth")
+
// Get the state
state := c.Query("state")
@@ -352,10 +223,20 @@ func (v *Verifier) VerifierAPIAuthenticationResponse(c *fiber.Ctx) error {
v.server.logger.Infof("Authenticate for state '%s' with %s", state, body)
// Decode into a map
- vc := &verifiableCredential{}
+ vc := &VerifiableCredential{}
json.Unmarshal(body, vc)
// Validate the credential
+ res, err := v.verifyCredential(*vc.Credential)
+ if err != nil {
+ v.server.logger.Errorw("Was not able to verify credential.", zap.Error(err))
+ return err
+ }
+ if !res {
+ v.server.logger.Info("Credential is not valid.")
+ return c.Status(http.StatusUnauthorized).JSON(verficationMsg{"Credential is invalid."})
+ }
+
v.server.logger.Infof("Store credential %s", *vc.Credential)
// Set the credential in storage, and wait for the polling from client
v.server.storage.Set(state, *vc.Credential, 10*time.Second)
@@ -366,10 +247,6 @@ func (v *Verifier) VerifierAPIAuthenticationResponse(c *fiber.Ctx) error {
func (v *Verifier) VerifierPageDisplayQR(c *fiber.Ctx) error {
- if sameDevice {
- return v.VerifierPageStartSIOPSameDevice(c)
- }
-
// Generate the state that will be used for checking expiration
state := generateNonce()
@@ -387,9 +264,7 @@ func (v *Verifier) VerifierPageDisplayQR(c *fiber.Ctx) error {
// Render index
m := fiber.Map{
- "issuerPrefix": issuerPrefix,
"verifierPrefix": verifierPrefix,
- "walletPrefix": walletPrefix,
"qrcode": qrCode1,
"prefix": verifierPrefix,
"state": state,