πŸ‘¨β€πŸ’» Verify User

To validate the end user, you can use the userVerificationToken from the login response.

The userVerificationToken is a JWT token that contains the faceUserId and the ecdsaPublicKey and eddsaPublicKey to verify the wallet address. From each public keys, you can derive the wallet address that you want to verify.

When fetching JWKs, use the link https://api.test.facewallet.xyz/jwks for the testnet, and the link https://api.facewallet.xyz/jwks for the mainnet.

The below is an example code for verifying the user.

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import java.net.URL;
import java.security.interfaces.RSAPublicKey;
import java.util.List;
import java.util.Map;

public class Example {

	// for mainnet: https://api.facewallet.xyz/jwks
  private static final String JWKS_URL = "https://api.stage-test.facewallet.xyz/jwks";
  private static final String USER_ID = "cdc97777-d5ff-45e3-83af-c58581747c93";
  private static final String ECDSA_PUBLIC_KEY = "0x3fe75ea9049a15ba6017ff32d0ff553263af06662332530494b1ffc09513674e40afa4a864650a93ec6470396f8608d23b232af3868d580c42ff6b72a095d0bc";
  private static final String EDDSA_PUBLIC_KEY = "0x11e5c1f5c2e1d697dfa0f4b01de692119816d60551eeb6be078d6791a744d9a4";


  public void VerifyToken() throws Exception {
    String token = "eyJraWQiOiJWZ3N3M3FrbXdwMUtYMmNEbHJtUzNSX2NXNzVZZTJQb0ZhblkwQjg3THkwIiwiYWxnIjoiUlMyNTYifQ.eyJpYXQiOjE2OTE5OTQ3NDgsImV4cCI6MTY5MjU5OTU0OCwid2FsbGV0cyI6W3siZWNkc2FQdWJsaWNLZXkiOiIweDNmZTc1ZWE5MDQ5YTE1YmE2MDE3ZmYzMmQwZmY1NTMyNjNhZjA2NjYyMzMyNTMwNDk0YjFmZmMwOTUxMzY3NGU0MGFmYTRhODY0NjUwYTkzZWM2NDcwMzk2Zjg2MDhkMjNiMjMyYWYzODY4ZDU4MGM0MmZmNmI3MmEwOTVkMGJjIiwiZWRkc2FQdWJsaWNLZXkiOiIweDExZTVjMWY1YzJlMWQ2OTdkZmEwZjRiMDFkZTY5MjExOTgxNmQ2MDU1MWVlYjZiZTA3OGQ2NzkxYTc0NGQ5YTQifV0sInVzZXJJZCI6ImNkYzk3Nzc3LWQ1ZmYtNDVlMy04M2FmLWM1ODU4MTc0N2M5MyJ9.N-lUB10rfI4zpIo-Tmzj82HrhP0YC29Sb7zcH_NV5JGVuzpLMPx2oZqiBkfjcqoPOYCUtDX3JFSBayzAgY47YSZpWfiNSNthd7l9OaS80bAhcR5s4qJTSuya60FA9NziKbS1rwU45EsNwGIcZNWXe8FQEmDAo37bbfdYHmvijGkQY-ksf6wt6L8dXYv1r41mI1rrgZJIYdblI4w_c_IrD3KXkpWAQVeOo0evku_n_ooJWmMpuoF3Nf4pyFfiH-V-EsZkOxOHhODizjiByCvWJvNQsiaAVkufv8ctK9w14fHl1nEiVr8fzHZWLc5yLH5Ehsp-mXILMujueHUgjrnkxQ";

    DecodedJWT jwt = JWT.decode(token);
    String kid = jwt.getKeyId();

    JwkProvider provider = new UrlJwkProvider(new URL(JWKS_URL));
    Jwk jwk = provider.get(kid);
    Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);

    JWTVerifier verifier = JWT.require(algorithm)
        .build();

    DecodedJWT verifiedJwt = verifier.verify(token);

    String userId = verifiedJwt.getClaim("userId").asString();
    List<Map> wallets = verifiedJwt.getClaim("wallets").asList(Map.class);

    if (!USER_ID.equals(userId)) {
      throw new Exception("User ID did not match");
    }

    for (Map<String, String> wallet : wallets) {
      String ecdsaPublicKey = wallet.get("ecdsaPublicKey");
      String eddsaPublicKey = wallet.get("eddsaPublicKey");
      if (!ECDSA_PUBLIC_KEY.equals(ecdsaPublicKey) || !EDDSA_PUBLIC_KEY.equals(eddsaPublicKey)) {
        throw new Exception("Wallet keys did not match");
      }
    }
  }
}
import jwt from "jsonwebtoken";
import jwkToPem from "jwk-to-pem";

const face = new Face({
  newwork: Network.TYPE_SUPPORTED_NETWORK,
  apiKey: 'YOUR_DAPP_API_KEY'
})
const user = await face!.auth.getCurrentUser();
const userIdToVerify = user!.faceUserId;
const walletToVerify = user!.wallet!;

const token = user!.userVerificationToken;

const decoded = jwt.decode(token, { complete: true, json: true });

const kid = decoded!.header.kid;
const userId = decoded!.payload.userId;
const wallets = decoded!.payload.wallets;

// for mainnet: https://api.facewallet.xyz/jwks
const response = await fetch("https://api.test.facewallet.xyz/jwks");
const { keys } = await response.json();
const key = keys.find((key: Record<string, string>) => key.kid === kid);
const jwk = jwkToPem(key);

jwt.verify(token, jwk, { algorithms: ["RS256"] });

if (
  userId !== userIdToVerify ||
  wallets.some(
    (wallet: { ecdsaPublicKey: string; eddsaPublicKey: string }) =>
    wallet.ecdsaPublicKey === walletToVerify.ecdsaPublicKey &&
    wallet.eddsaPublicKey === walletToVerify.eddsaPublicKey
  )
) {
  throw new Error('invalid user id');
}
package main

import (
	"io"
	"log"
	"net/http"

	"github.com/lestrrat-go/jwx/v2/jwk"
	"github.com/lestrrat-go/jwx/v2/jwt"
)

func main() {
	sampleUserToken := "eyJraWQiOiJWZ3N3M3FrbXdwMUtYMmNEbHJtUzNSX2NXNzVZZTJQb0ZhblkwQjg3THkwIiwiYWxnIjoiUlMyNTYifQ.eyJpYXQiOjE3MDQ2NzkzODEsImV4cCI6MTcwNTg4ODk4MSwid2FsbGV0cyI6W3siZWNkc2FQdWJsaWNLZXkiOiIweDQwMzk4M2M0OTNlODc1MDkxMmQ1MjYwZGY4MTJiNDg2ODUzYzE2NmY1Yjk3MmNhOWRlM2Q5NGNlMmNiYWMwYWRkZjM2NjRlOWQ1MmE3MjM4YWE3MWMyMGJkM2M3ODc2ZGM3NTRkOWE2ODdhNDhmMDdhNjFiZGFkZDJkOWEwMzlhIiwiZWRkc2FQdWJsaWNLZXkiOiIweGIyOGNhMzc4ZjQwMWQ3NDRkZDZhOTMzZDI5MzBmZTllZjUwNDUzYWNmMTEwMDFiY2QxMGExYzI0MjE1ODMyMTUifV0sInVzZXJJZCI6IjllZGRjMjBjLTc5YWUtNDY1Ni04YzI3LTViZTcwMmJhNDViZiJ9.L1fN-DnGB2OLPwoYxRog_RrpgMQ_hM_EoBsqXz-mU6o11b4gPPdCbVmlEAvsLFWpC--PxZcKlFpZ5w_gT8Ka9DBIIvy6Ok5Rxfm7gGZGu1T2o5nTQkwyvbOZWcj-xrpLrLULp8TcE_SGgef6s58bLJJIuFXRefFC-vmTN2IAFGMfKS9tYu5nZ_asN1aixl9f1H_XCH-4kfM8T_je1uH9CMyf6Gt---qQEg4oMvNfPGUdWMIqaMmHkfe45BX7nTDciwclb7yjXarSXGllz1AAU4kf1WRqdhjfN8F44Th1K3o068jsykp5Ynp_E0nZFYFHe3Yr3GM9lR7Ps0RMFxOVUw"
	jwkApiUrl := "https://api.test.facewallet.xyz/jwks" // for mainnet: https://api.facewallet.xyz/jwks
	client := &http.Client{}
	newReq, err := http.NewRequest("GET", jwkApiUrl, nil)
	if err != nil {
		panic(err)
	}
	resp, err := client.Do(newReq)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	set, err := jwk.Parse(respBody)

	if err != nil {
		panic(err)
	}
	token, err := jwt.ParseString(sampleUserToken, jwt.WithKeySet(set))

	if err != nil {
		panic(err)
	}

	log.Printf("to verify user id : %s\n", token.PrivateClaims()["userId"])
	if wallets, ok := token.PrivateClaims()["wallets"]; ok {
		for _, wallet := range wallets.([]interface{}) {
			walletMap := wallet.(map[string]interface{})
			log.Printf("to verify user ecdsa public key : %s\n", walletMap["ecdsaPublicKey"])
			log.Printf("to verify user eddsa public key : %s\n", walletMap["eddsaPublicKey"])
			log.Printf("------------------------------------\n")
		}
		// sample output
		// to verify user id : 9eddc20c-79ae-4656-8c27-5be702ba45bf
		// to verify user ecdsa public key : 0x403983c493e8750912d5260df812b486853c166f5b972ca9de3d94ce2cbac0addf3664e9d52a7238aa71c20bd3c7876dc754d9a687a48f07a61bdadd2d9a039a
		// to verify user eddsa public key : 0xb28ca378f401d744dd6a933d2930fe9ef50453acf11001bcd10a1c2421583215
		//
		// now, you can compare this verified output and sdk output
	} else {
		panic("cannot find wallets")
	}
}