📘

full code repo https://github.com/lens-protocol/lens-api-examples

To login it takes two server actions: the first is to generate a challenge from the server, and the second is to sign that challenge with your Ethereum wallet and send the signature to our server to generate a valid JWT access token, and refresh token.

Challenge

To get a JWT token, you must first request a challenge from the server, which will return you some text to sign with the wallet to prove ownership. Please note the challenge will only last 5 minutes before it expires, if it expires you must generate a new challenge. Once you have used the challenge to generate a JWT token it will not work again.

API details

query Challenge {
  challenge(request: { address: "0xdfd7D26fd33473F475b57556118F8251464a24eb" }) {
    text
  }
}
{
  "data": {
    "challenge": {
      "text": "I want to authenticate with lens and generate a JWT token at timestamp - 1645102996447. Auth request id - 6a01ffa229be678f03d705eb9b4c454554e2cef4be2c273fc0c9ed5be8762625"
    }
  }
}
type Query {
  challenge(request: ChallengeRequest!): AuthChallengeResult!
}
# The challenge request
input ChallengeRequest {
  # The ethereum address you want to login with
  address: EthereumAddress!
}
# The auth challenge result
type AuthChallengeResult {
  # The text to sign
  text: String!
}

Full code example

import { apolloClient } from './apollo-client';
import { gql } from '@apollo/client'

const GET_CHALLENGE = `
  query($request: ChallengeRequest!) {
    challenge(request: $request) { text }
  }
`

export const generateChallenge = (address) => {
   return apolloClient.query({
    query: gql(GET_CHALLENGE),
    variables: {
      request: {
         address,
      },
    },
  })
}
// this is showing you how you use it with react for example
// if your using node or something else you can import using
// @apollo/client/core!
import { ApolloClient, InMemoryCache } from '@apollo/client'

const APIURL = 'https://api-mumbai.lens.dev/'

export const apolloClient= new ApolloClient({
  uri: APIURL,
  cache: new InMemoryCache(),
})

Authenticate

Once you get the challenge text from the server you need to sign the challenge with your ethereum wallet and then pass that signature to the authentication endpoint, this will generate you an accountToken and a refreshToken.

  • accessToken - This lasts 30 minutes before needed to be refreshed
  • refreshToken - This lasts 1 day to allow you to keep them logged in and generate a new accessToken when they come back without them having to sign a challenge again.

API details

mutation Authenticate {
  authenticate(request: {
    address: "0xdfd7D26fd33473F475b57556118F8251464a24eb",
    signature: "0x8f82e1a2c2cc35a2963c60eeb0a76aecc100686c4ffcb98fd522a90cba2f0b2642067c79cd6d0c9d239ed28a6882818f77bf546e774410236c730988bd14de5d1c"
  }) {
    accessToken
    refreshToken
  }
}
{
  "data": {
    "authenticate": {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjB4YjE5QzI4OTBjZjk0N0FEM2YwYjdkN0U1QTlmZkJjZTM2ZDNmOWJkMiIsInJvbGUiOiJub3JtYWwiLCJpYXQiOjE2NDUxMDQyMzEsImV4cCI6MTY0NTEwNjAzMX0.lwLlo3UBxjNGn5D_W25oh2rg2I_ZS3KVuU9n7dctGIU",
      "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjB4YjE5QzI4OTBjZjk0N0FEM2YwYjdkN0U1QTlmZkJjZTM2ZDNmOWJkMiIsInJvbGUiOiJyZWZyZXNoIiwiaWF0IjoxNjQ1MTA0MjMxLCJleHAiOjE2NDUxOTA2MzF9.2Tdts-dLVWgTLXmah8cfzNx7sGLFtMBY7Z9VXcn2ZpE"
    }
  }
}
type Mutation {
 authenticate(request: SignedAuthChallenge!): AuthenticationResult!
}
# The signed auth challenge
input SignedAuthChallenge {
  # The ethereum address you signed the signature with
  address: EthereumAddress!

  # The signature
  signature: Signature!
}
# The authentication result
type AuthenticationResult {
  # The access token
  accessToken: Jwt!

  # The refresh token
  refreshToken: Jwt!
}

Full code example

import { apolloClient } from './apollo-client';
import { gql } from '@apollo/client'

const AUTHENTICATION = `
  mutation($request: SignedAuthChallenge!) { 
    authenticate(request: $request) {
      accessToken
      refreshToken
    }
 }
`

export const authenticate = (address, signature) => {
   return apolloClient.mutate({
    mutation: gql(AUTHENTICATION),
    variables: {
      request: {
        address,
        signature,
      },
    },
  })
}
// this is showing you how you use it with react for example
// if your using node or something else you can import using
// @apollo/client/core!
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client'

const httpLink = new HttpLink({ uri: 'https://api-mumbai.lens.dev/' });

// example how you can pass in the x-access-token into requests using `ApolloLink`
const authLink = new ApolloLink((operation, forward) => {
  // Retrieve the authorization token from local storage.
  // if your using node etc you have to handle your auth different
  const token = localStorage.getItem('auth_token');

  // Use the setContext method to set the HTTP headers.
  operation.setContext({
    headers: {
      'x-access-token': token ? `Bearer ${token}` : ''
    }
  });

  // Call the next link in the middleware chain.
  return forward(operation);
});

export const apolloClient = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
})

Hooking login together all in one

So now you know how to generate a challenge and request authentication tokens let's see how this looks hooked together.

import { getAddress, signText } from './ethers.service';
import { generateChallenge } from './generate-challenge'
import { authenticate } from './authenticate'

export const login = async () => {
  // we grab the address of the connected wallet
    const address = await getAddress();
  
  // we request a challenge from the server
  const challengeResponse = await generateChallenge(address);
  
  // sign the text with the wallet
  const signature = await signText(challengeResponse.data.challenge.text)
  
  const accessTokens = await authenticate(address, signature);
  console.log(accessTokens);
  // you now have the accessToken and the refreshToken
  // {
  //  data: {
  //   authenticate: {
  //    accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjB4YjE5QzI4OTBjZjk0N0FEM2YwYjdkN0U1QTlmZkJjZTM2ZDNmOWJkMiIsInJvbGUiOiJub3JtYWwiLCJpYXQiOjE2NDUxMDQyMzEsImV4cCI6MTY0NTEwNjAzMX0.lwLlo3UBxjNGn5D_W25oh2rg2I_ZS3KVuU9n7dctGIU",
  //    refreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjB4YjE5QzI4OTBjZjk0N0FEM2YwYjdkN0U1QTlmZkJjZTM2ZDNmOWJkMiIsInJvbGUiOiJyZWZyZXNoIiwiaWF0IjoxNjQ1MTA0MjMxLCJleHAiOjE2NDUxOTA2MzF9.2Tdts-dLVWgTLXmah8cfzNx7sGLFtMBY7Z9VXcn2ZpE"
  //   }
  // }
}
import { ethers } from 'ethers';

// This code will assume you are using MetaMask.
// It will also assume that you have already done all the connecting to metamask
// this is purely here to show you how the public API hooks together
export const ethersProvider = new ethers.providers.Web3Provider(window.ethereum);

export const getAddress = async() => {
    const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
  return accounts[0];
}

export const signText = (text) => {
  return ethersProvider.signMessage(text);
}
import { apolloClient } from './apollo-client';
import { gql } from '@apollo/client'

const GET_CHALLENGE = `
  query($request: ChallengeRequest!) {
    challenge(request: $request) { text }
  }
`

export const generateChallenge = (address) => {
   return apolloClient.query({
    query: gql(GET_CHALLENGE),
    variables: {
      request: {
         address,
      },
    },
  })
}
import { apolloClient } from './apollo-client';
import { gql } from '@apollo/client'

const AUTHENTICATION = `
  mutation($request: SignedAuthChallenge!) { 
    authenticate(request: $request) {
      accessToken
      refreshToken
    }
 }
`

export const authenticate = (address, signature) => {
   return apolloClient.mutate({
    mutation: gql(AUTHENTICATION),
    variables: {
      request: {
        address,
        signature,
      },
    },
  })
}
// this is showing you how you use it with react for example
// if your using node or something else you can import using
// @apollo/client/core!
import { ApolloClient, InMemoryCache } from '@apollo/client'

const APIURL = 'https://api-mumbai.lens.dev/'

export const apolloClient= new ApolloClient({
  uri: APIURL,
  cache: new InMemoryCache(),
})

Did this page help you?