Skip to main content

GitHub

You can associate your GitHub account with your DEMOS address using one of two methods:
  1. OAuth Flow (Recommended) - Seamless authentication through the Key Server OAuth system
  2. Gist-based Verification - Manual proof creation via GitHub Gist

The OAuth flow provides a seamless user experience by authenticating through the Key Server, which handles the GitHub OAuth process and provides cryptographic attestation.
For detailed information about the Key Server OAuth system, including attestation verification and error handling, see the Key Server OAuth documentation.

1. Verify GitHub Account Ownership

Use the KeyServerClient to verify the user owns a GitHub account:
import { Demos } from "@kynesyslabs/demosdk/websdk";
import { KeyServerClient } from "@kynesyslabs/demosdk/keyserver";

const demos = new Demos();
await demos.connect("https://node2.demos.sh");

const client = new KeyServerClient({
    endpoint: "https://node2.demos.sh",
    nodePubKey: "node_public_key_hex",
});

// Complete OAuth verification with optional wallet binding
const result = await client.verifyOAuth("github", {
    onAuthUrl: (url) => {
        // Open GitHub authorization in a popup
        window.open(url, "github-oauth", "width=600,height=700");
    },
    onPoll: (attempt, status) => {
        console.log(`Verification status: ${status}`);
    },
    timeout: 300000, // 5 minutes
    // Optional: Bind wallet address to this verification for sybil resistance
    walletBinding: async (state) => {
        const message = `demos-oauth-bind:${state}`;
        const signature = await wallet.signMessage(message);
        return { address: wallet.address, signature, signatureType: "evm" };
    },
});

console.log("Verified user:", result.user.username);
console.log("Provider ID:", result.user.providerId);

2. Submit Identity Transaction

After successful verification, use the attestation to create an identity transaction:
import { Identities } from "@kynesyslabs/demosdk/abstraction";

await demos.connectWallet(mnemonic);

const identities = new Identities();
const validityData = await identities.addGithubIdentityFromOAuth(
    demos,
    {
        attestation: {
            provider: "github",
            userId: result.user.providerId,
            username: result.user.username,
            timestamp: result.user.verifiedAt,
            nodePublicKey: result.attestation.metadata.nodePubKey
        },
        signature: result.attestation.signature.data,
        signatureType: "ed25519"
    }
);

if (validityData.result === 200) {
    const res = await demos.broadcast(validityData);
    console.log(res);
}

3. Verify Attestation (Optional)

You can cryptographically verify the attestation before submitting:
import { verifyOAuthAttestation } from "@kynesyslabs/demosdk/keyserver";

const verification = verifyOAuthAttestation(result, KNOWN_KEY_SERVER_PUBKEY);

if (!verification.valid) {
    throw new Error(`Invalid attestation: ${verification.reason}`);
}
The OAuth attestation is cryptographically signed by the Key Server, ensuring the identity verification is authentic. See Attestation Verification for details.
Use Wallet Binding to cryptographically link the OAuth identity to a specific wallet address. This provides sybil resistance by proving the user owns both the social account and the wallet.

Method 2: Gist-based Verification

This method requires manually creating a public gist containing a cryptographic proof.

1. Connect your wallet

import { Demos } from "@kynesyslabs/demosdk/websdk";
import { Identities } from "@kynesyslabs/demosdk/abstraction";

// the DEMOS rpc
const rpc = "https://node2.demos.sh"

const demos = new Demos();
await demos.connect(rpc);
await demos.connectWallet(mnemonic);

2. Generate the proof payload

To link your GitHub account to your DEMOS address, you need to create a public gist containing a proof on your GitHub. The proof is a string that contains a message, a signature and your public key. You can generate a proof as shown below:
const identities = new Identities();

const proofPayload = await identities.createWeb2ProofPayload(demos)
console.log("Upload on GitHub gist: ", proofPayload)
The proof looks like this:
demos:dw2p:ed25519:e9dd684a031e142ce312b695275b226ab8824f2fd3674db52f28be6c3e9fe027f88a8a/...
Open your GitHub and create a new public gist containing only the proof in the first file.

3. Send an identity request

After creating the gist, copy the gist url and use it to create an identity transaction as shown below:
const proof = "https://gist.github.com/cwilvx/abf8db960c16dfc7f6dc1da840852f79"

const validityData = await identities.addGithubIdentity(demos, proof)
console.log("validity data: ", validityData)

if (validityData.result == 200){
    const res = await demos.broadcast(validityData)
    console.log(res)
}
The following proof url formats are also supported:
  1. Raw gist url: https://gist.githubusercontent.com/cwilvx/abf8db960c16dfc7f6dc1da840852f79/raw/224478424c5e6e51f5eb60cb6aeea278d3418742/gistfile1.txt
  2. Raw proof file in a GitHub repo url: https://raw.githubusercontent.com/cwilvx/vonage-draft-images/refs/heads/master/proof.txt
A successful transaction response should look like this:
{
  "result": 200,
  "response": {
    "message": "Verified github proof. Transaction applied."
  },
  "require_reply": false,
  "extra": {
    "confirmationBlock": 38
  }
}

Getting linked web2 accounts

After the confirmation block has been forged, you can retrieve all your linked accounts as shown:
const web2Ids = await identities.getWeb2Identities()
The response should look like this:
{
  "result": 200,
  "response": {
    "github": [
      {
        "proof": "https://gist.github.com/cwilvx/abf8db960c16dfc7f6dc1da840852f79",
        "username": "cwilvx",
        "proofHash": "c9bcf4dcb2bdb490...1060ab12b16a7385351"
      }
    ]
  },
  "require_reply": false,
  "extra": null
}

Removing linked GitHub identity

You can create a transaction to remove your linked GitHub account as shown:
const payload = {
    context: "github",
    username: "cwilvx"
}

const validityData = await identities.removeWeb2Identity(demos, payload);

if (validityData.result == 200) {
    const res = await demos.broadcast(validityData);
    console.log(res);
}
A successful transaction response should look like this:
{
  "result": 200,
  "response": {
    "message": "Identity removed. Transaction applied."
  },
  "require_reply": false,
  "extra": {
    "confirmationBlock": 47
  }
}

Comparison of Methods

FeatureOAuth FlowGist-based
User ExperienceSeamless, one-clickManual gist creation
Cryptographic AttestationDAHR attestation with signatureProof in gist
Verification SpeedInstantRequires gist fetch
Best ForProduction appsDevelopment/testing