Links

Integration and Usage

In this example we use go-chi to build the RESTful service. You can view the full code here, and find the SDK here.

Initialization

Add the following in the go.mod file.
require ( ... github.com/ontology-tech/ontlogin-sdk-go latest)

Add API Methods

Import the methods in the main.go file.
  • requestChallenge: Returns the challenge from the server
  • submitChallenge : Passes the signed challenge and VP (if requested by the server)
package main​
import (
"log"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"ontlogin-sample/auth"
"ontlogin-sample/service"
)
Initialize the service, perform cross-origin resource sharing checks and define API methods.
func main() {
r := chi.NewRouter()
service.InitService() // Service Initialization
r.Use(cors.Handler(cors.Options{
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
AllowedOrigins: []string{"*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
// AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
AllowedHeaders: []string{"Authorization", "Content-Length", "X-CSRF-Token", "Token", "session", "X_Requested_With", "Accept", "Origin", "Host", "Connection", "Accept-Encoding", "Accept-Language", "DNT", "X-CustomHeader", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Pragma"},
ExposedHeaders: []string{"Content-Length", "token", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Cache-Control", "Content-Language", "Content-Type", "Expires", "Last-Modified", "Pragma", "FooBar"},
AllowCredentials: false,
MaxAge: 172800, // Maximum value not ignored by any of major browsers
//Debug:true,
}))
r.Use(middleware.Logger)
r.Use(auth.Middleware()) // Detect app login permission​
r.Post("/requestChallenge", service.RequestChallenge) // Challenge request
r.Post("/submitChallenge",service.Login) // Challenge submission
r.Get("/afterLogin",service.AfterLogin) // Other business logic
log.Fatal(http.ListenAndServe(":3000", r))
}

Import and Use service.go

Handle API requests using service.go
package service​
import (
"encoding/json"
"fmt"
"github.com/ontology-tech/ontlogin-sdk-go/did"
"github.com/ontology-tech/ontlogin-sdk-go/did/ont"
"github.com/ontology-tech/ontlogin-sdk-go/modules"
ontloginsdk "github.com/ontology-tech/ontlogin-sdk-go/sdk"
"net/http"
"github.com/google/uuid"
"ontlogin-sample/auth"
"ontlogin-sample/jwt"
)
var loginsdk *ontloginsdk.OntLoginSdk
var mapstore map[string]string
func InitService() {
mapstore = make(map[string]string) // To store UUID. In real projects, UUID can be stored in the database, redis or cache
vcfilters := make(map[string][]*modules.VCFilter) // Configure `VCFilter` according to `actionType`
vcfilters[modules.ACTION_REGISTER] = []*modules.VCFilter{
{Type: "EmailCredential", Required: true, TrustRoots: []string{"did:ont:ssssss"}}, // Issuer DID
} // Server configuration information needed by the SDK, which can be obtained from config files
conf := &ontloginsdk.SDKConfig{
Chain: []string{"ont"}, // Supported chains, e.g. eth, ont, bsc, respective Processors needed
Alg: []string{"ES256"}, // Supported signature schemes
ServerInfo: &modules.ServerInfo{
Name: "testServcer",
Icon: "http://somepic.jpg",
Url: "https://ont.io",
Did: "did:ont:sampletest",
VerificationMethod: "",
},
VCFilters: vcfilters, // VC required by the server
}
Processors := make(map[string]did.DidProcessor) // Initialize the Processor of the respective chain
// Parameter specification using Ontology as an example
// 1. doubleDirection bool: if mutual authentication is required
// 2. Ontology node rpc server address
// 3. DID contract address, can be null when 1 takes `false`
// 4. Ontology wallet address, can be null when 1 takes `false`
// 5. Wallet password, can be null when 1 takes `false`
ontProcessor, err := ont.NewOntProcessor(false, "http://polaris2.ont.io:20336", "52df370680de17bc5d4262c446f102a0ee0d6312", "./wallet.dat", "123456")
if err != nil {
panic(err)
}
Processors["ont"] = ontProcessor
// Except config, Processor and SDK, the following functions need to be passed
// 1. func()string: generates UUID
// 2. func(string)error: checks if the nonce (UUID) exists in the database/redis/cache
loginsdk, err = ontloginsdk.NewOntLoginSdk(conf, Processors, GenUUID, CheckNonce)
if err != nil {
panic(err)
}
}
func RequestChallenge(writer http.ResponseWriter, request *http.Request){
// Handle the request from the client
cr := &modules.ClientHello{}
writer.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(request.Body).Decode(&cr)
if err != nil {
fmt.Printf("err:%s\n",err.Error())
writer.Write([]byte(err.Error()))
return
}
// Invoke the SDK to generate the challenge
serverHello,err := loginsdk.GenerateChallenge(cr)
if err!= nil{
fmt.Printf("err:%s\n",err.Error())
writer.Write([]byte(err.Error()))
return
}
// Return the challenge
bts,_:=json.Marshal(serverHello)
writer.Write(bts)
}
func Login(writer http.ResponseWriter, request *http.Request){
lr := &modules.ClientResponse{}
writer.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(request.Body).Decode(&lr)
if err != nil {
fmt.Printf("err:%s\n",err.Error())
writer.Write([]byte(err.Error()))
return
}
err = loginsdk.ValidateClientResponse(lr)
if err != nil {
fmt.Printf("err:%s\n",err.Error())
writer.Write([]byte(err.Error()))
return
}
// The challenge response from the client has passed validation
// Now you can proceed according to your business logic
// In this example, JWT is used for authentication​​
s ,err:= jwt.GenerateToken(lr.Did)
writer.Write([]byte(s))
}
func AfterLogin(writer http.ResponseWriter, request *http.Request) {
if err := auth.CheckLogin(request.Context()); err != nil {
fmt.Printf("err:%s\n", err.Error())
writer.Write([]byte("please login first"))
return
}
writer.Write([]byte("normal business process"))
}
​​func GenUUID()string{
uuid,err := uuid.NewUUID()
if err != nil{
fmt.Printf("uuid failed:%s\n",err.Error())
return ""
}
mapstore[uuid.String()] = "ok"
return uuid.String()
}
func CheckNonce(nonce string)error{
if _,ok:=mapstore[nonce]
;!ok{
return fmt.Errorf("no nonce found")
}
return nil
}

Handle VP

Use the following to extract VC from VP in form of JSON text.
GetCredentialJson(chain, presentation string) ([]string, error)
Since the form of VC varies according to the server's request, only JSON is supported here. The server can parse the VC into the per-defined form.