-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #289 from getyoti/SDK-2235-share-v-2-retrieve-receipt
Sdk 2235 share v 2 retrieve receipt
- Loading branch information
Showing
79 changed files
with
4,708 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
YOTI_CLIENT_SDK_ID= | ||
YOTI_KEY_FILE_PATH= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
/images/YotiSelfie.jpeg | ||
|
||
# Example project generated self-signed certificate | ||
/yotiSelfSignedCert.pem | ||
/yotiSelfSignedKey.pem | ||
|
||
# Compiled binary | ||
/digitalidentity |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
## Table of Contents | ||
|
||
1) [Setup](#setup) - | ||
How to initialise the Yoti client | ||
|
||
1) [Running the digitalidentity examples](#running-the-profile-example) - | ||
Running the digitalidentity example | ||
|
||
## Setup | ||
|
||
The YotiClient is the SDK entry point. To initialise it you need include the following snippet inside your endpoint initialisation section: | ||
|
||
```Go | ||
clientSdkID := "your-client-sdk-id" | ||
key, err := os.ReadFile("path/to/your-application-pem-file.pem") | ||
if err != nil { | ||
// handle key load error | ||
} | ||
|
||
client, err := yoti.NewClient( | ||
clientSdkID, | ||
key) | ||
``` | ||
|
||
Where: | ||
|
||
* `"your-client-sdk-id"` is the SDK Client Identifier generated by Yoti Hub in the Key tab when you create your application. | ||
|
||
* `path/to/your-application-pem-file.pem` is the path to the application pem file. It can be downloaded from the Keys tab in the [Yoti Hub](https://hub.yoti.com/). | ||
|
||
Please do not open the pem file as this might corrupt the key, and you will need regenerate your key. | ||
|
||
Keeping your settings and access keys outside your repository is highly recommended. You can use a package like [godotenv](https://github.com/joho/godotenv) to manage environment variables more easily. | ||
|
||
|
||
## Running the DigitalIdentity Example | ||
|
||
1. Change directory to the profile example folder: `cd _examples/digitalidentity` | ||
2. On the [Yoti Hub](https://hub.yoti.com/): | ||
1. Set the application domain of your app to `localhost:8080` | ||
2. Set the scenario callback URL to `/digitalidentity` | ||
3. Rename the [.env.example](_examples/digitalidentity/.env.example) file to `.env` and fill in the required configuration values (mentioned in the [Configuration](#configuration) section) | ||
4. Build with `go build` | ||
5. Start the compiled program by running `./digitalidentity` | ||
|
||
Visiting `https://localhost:8080/` should show a webpage with a Yoti button rendered on it |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/pem" | ||
"fmt" | ||
"log" | ||
"math/big" | ||
"net" | ||
"os" | ||
"strings" | ||
"time" | ||
) | ||
|
||
var ( | ||
validFrom = "" | ||
validFor = 2 * 365 * 24 * time.Hour | ||
isCA = true | ||
rsaBits = 2048 | ||
) | ||
|
||
func publicKey(priv interface{}) interface{} { | ||
switch k := priv.(type) { | ||
case *rsa.PrivateKey: | ||
return &k.PublicKey | ||
case *ecdsa.PrivateKey: | ||
return &k.PublicKey | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
func pemBlockForKey(priv interface{}) *pem.Block { | ||
switch k := priv.(type) { | ||
case *rsa.PrivateKey: | ||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} | ||
case *ecdsa.PrivateKey: | ||
b, err := x509.MarshalECPrivateKey(k) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) | ||
os.Exit(2) | ||
} | ||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
func certificatePresenceCheck(certPath string, keyPath string) (present bool) { | ||
if _, err := os.Stat(certPath); os.IsNotExist(err) { | ||
return false | ||
} | ||
if _, err := os.Stat(keyPath); os.IsNotExist(err) { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func generateSelfSignedCertificate(certPath, keyPath, host string) error { | ||
priv, err := rsa.GenerateKey(rand.Reader, rsaBits) | ||
if err != nil { | ||
log.Printf("failed to generate private key: %s", err) | ||
return err | ||
} | ||
|
||
notBefore, err := parseNotBefore(validFrom) | ||
if err != nil { | ||
log.Printf("failed to parse 'Not Before' value of cert using validFrom %q, error was: %s", validFrom, err) | ||
return err | ||
} | ||
|
||
notAfter := notBefore.Add(validFor) | ||
|
||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) | ||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | ||
if err != nil { | ||
log.Printf("failed to generate serial number: %s", err) | ||
return err | ||
} | ||
|
||
template := x509.Certificate{ | ||
SerialNumber: serialNumber, | ||
Subject: pkix.Name{ | ||
Organization: []string{"Yoti"}, | ||
}, | ||
NotBefore: notBefore, | ||
NotAfter: notAfter, | ||
|
||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||
BasicConstraintsValid: true, | ||
} | ||
|
||
hosts := strings.Split(host, ",") | ||
for _, h := range hosts { | ||
if ip := net.ParseIP(h); ip != nil { | ||
template.IPAddresses = append(template.IPAddresses, ip) | ||
} else { | ||
template.DNSNames = append(template.DNSNames, h) | ||
} | ||
} | ||
|
||
if isCA { | ||
template.IsCA = true | ||
template.KeyUsage |= x509.KeyUsageCertSign | ||
} | ||
|
||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) | ||
if err != nil { | ||
log.Printf("Failed to create certificate: %s", err) | ||
return err | ||
} | ||
|
||
err = createPemFile(certPath, derBytes) | ||
if err != nil { | ||
log.Printf("failed to create pem file at %q: %s", certPath, err) | ||
return err | ||
} | ||
log.Printf("written %s\n", certPath) | ||
|
||
err = createKeyFile(keyPath, priv) | ||
if err != nil { | ||
log.Printf("failed to create key file at %q: %s", keyPath, err) | ||
return err | ||
} | ||
log.Printf("written %s\n", keyPath) | ||
|
||
return nil | ||
} | ||
|
||
func createPemFile(certPath string, derBytes []byte) error { | ||
certOut, err := os.Create(certPath) | ||
|
||
if err != nil { | ||
log.Printf("failed to open "+certPath+" for writing: %s", err) | ||
return err | ||
} | ||
|
||
defer certOut.Close() | ||
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) | ||
|
||
return err | ||
} | ||
|
||
func createKeyFile(keyPath string, privateKey interface{}) error { | ||
keyOut, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) | ||
|
||
if err != nil { | ||
log.Print("failed to open "+keyPath+" for writing:", err) | ||
return err | ||
} | ||
|
||
defer keyOut.Close() | ||
err = pem.Encode(keyOut, pemBlockForKey(privateKey)) | ||
|
||
return err | ||
} | ||
|
||
func parseNotBefore(validFrom string) (notBefore time.Time, err error) { | ||
if len(validFrom) == 0 { | ||
notBefore = time.Now() | ||
} else { | ||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", validFrom) | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err) | ||
return time.Time{}, err | ||
} | ||
} | ||
|
||
return notBefore, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package main | ||
|
||
import ( | ||
"html/template" | ||
"log" | ||
"net/http" | ||
) | ||
|
||
func errorPage(w http.ResponseWriter, r *http.Request) { | ||
templateVars := map[string]interface{}{ | ||
"yotiError": r.Context().Value(contextKey("yotiError")).(string), | ||
} | ||
log.Printf("%s", templateVars["yotiError"]) | ||
t, err := template.ParseFiles("error.html") | ||
if err != nil { | ||
panic(errParsingTheTemplate + err.Error()) | ||
} | ||
|
||
err = t.Execute(w, templateVars) | ||
if err != nil { | ||
panic(errApplyingTheParsedTemplate + err.Error()) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>Yoti Example Project - Error</title> | ||
</head> | ||
<body style="text-align: center; margin: 200px 0"> | ||
<h1>An Error Occurred</h1> | ||
<p><b>Error: </b>{{.yotiError}}</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module digitalidentity | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/getyoti/yoti-go-sdk/v3 v3.0.0 | ||
github.com/joho/godotenv v1.3.0 | ||
) | ||
|
||
require google.golang.org/protobuf v1.30.0 // indirect | ||
|
||
replace github.com/getyoti/yoti-go-sdk/v3 => ../../ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= | ||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= | ||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<!DOCTYPE html> | ||
<html class="yoti-html" lang="en"> | ||
<head> | ||
<meta charset="utf-8"/> | ||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/> | ||
<title>Yoti client example</title> | ||
<link rel="stylesheet" type="text/css" href="/static/index.css"/> | ||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet"/> | ||
</head> | ||
<body class="yoti-body"> | ||
<main> | ||
<section class="yoti-top-section"> | ||
<div class="yoti-logo-section"> | ||
<img class="yoti-logo-image" src="/static/assets/logo.png" srcset="/static/assets/[email protected] 2x" alt="Yoti"/> | ||
</div> | ||
|
||
<h2 th:text="${message}">Digital Identity Share Example page</h2> | ||
|
||
<p> SdkId: <strong th:text={{.yotiClientSdkID}}>{{.yotiClientSdkID}}</strong></p> | ||
|
||
<div class="yoti-sdk-integration-section"> | ||
<div id="webshare-target"></div> | ||
</div> | ||
|
||
</section> | ||
|
||
<section class="yoti-sponsor-app-section"> | ||
<h3 class="yoti-sponsor-app-header">The Yoti app is free to download and use:</h3> | ||
|
||
<div class="yoti-store-buttons-section"> | ||
<a href="https://itunes.apple.com/us/app/yoti/id983980808?ls=1&mt=8" class="yoti-app-button-link"> | ||
<img src="/static/assets/app-store-badge.png" srcset="/static/assets/[email protected] 2x" | ||
alt="Download on the App Store"/> | ||
</a> | ||
|
||
<a href="https://play.google.com/store/apps/details?id=com.yoti.mobile.android.live" | ||
class="yoti-app-button-link"> | ||
<img src="/static/assets/google-play-badge.png" srcset="/static/assets/[email protected] 2x" | ||
alt="get it on Google Play"/> | ||
</a> | ||
</div> | ||
</section> | ||
</main> | ||
<script> | ||
async function onSessionIdResolver() { | ||
console.log('sessioning') | ||
const response = await fetch('https://localhost:8080/v2/generate-share', {method: 'GET'}) | ||
const data = await response.json() | ||
console.log('data: %s', data.id) | ||
return data.id | ||
} | ||
|
||
async function completionHandler(receivedReceiptId) { | ||
console.log('completion handler:', receivedReceiptId) | ||
const response = await fetch('/v2/receipt-info?ReceiptID=' + encodeURIComponent(receivedReceiptId) , {method: 'GET'}) | ||
} | ||
|
||
function onErrorListener(...data) { | ||
console.warn('onErrorListener:', ...data) | ||
} | ||
|
||
async function onReadyToStart() { | ||
const {Yoti} = window | ||
await Yoti.createWebShare({ | ||
name: 'Use Yoti', | ||
domId: 'webshare-target', | ||
sdkId: '{{.yotiClientSdkID}}', | ||
hooks: { | ||
sessionIdResolver: onSessionIdResolver, | ||
errorListener: onErrorListener, | ||
completionHandler, | ||
}, | ||
flow: "REVEAL_MODAL" | ||
}) | ||
} | ||
|
||
async function onClientLoaded() { | ||
const {Yoti} = window | ||
await Yoti.ready() | ||
await onReadyToStart() | ||
} | ||
</script> | ||
<script src="https://www.yoti.com/share/client/v2" onload="onClientLoaded()"></script> | ||
</body> | ||
</html> |
Oops, something went wrong.