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.

Last updated