@nextdapp/account

This is all about how to develop this next-dapp account plugin, not how to develop a next-dapp based project itself.

Usage no npm install needed!

<script type="module">
  import nextdappAccount from 'https://cdn.skypack.dev/@nextdapp/account';
</script>

README

How to install and develop Account plugin

This is all about how to develop this next-dapp account plugin, not how to develop a next-dapp based project itself.

Next Dapp itself is based upon Next.js / React and heavily powered by Recoil state management library. Other major dependencies are Ramda and Rebass.

The core plugin is located at @nextdapp/core.

A working example can be found here.

You will need the nextdapp command line tool for development. Install it globally with npm or yarn.

I will use yarn throughout this document.

sudo npm i -g nextdapp

or

yarn global add nextdapp

1. Clone the repo & Install node modules

A demo directory is nested inside the plugin directory. They are separate projects, so you need to install the required packages for the both projects separately.

# clone
git clone https://github.com/warashibe/nd-account.git

# move to the plugin dixrectory and install node modules with yarn
cd nd-account && yarn

# move to the demo directory and install node modules with yarn
cd demo && yarn

2. Copy and Modify conf.sample.js file

nd/conf.js is ignored by .gitignore, so you need to copy nd/conf.sample.js to nd/conf.js and modify it according to your needs. For now, keep all the dummy data even if you don't use some login methods. The plugin throws an error, if something is missing. This behavior will be taken care of in the future.

cp nd/conf.sample.js nd/conf.js

Sample Settings [/nd/conf.js]

import { mergeAll } from "ramda"

let local = {}

try {
  local = require("nd/conf.local")
} catch (e) {}

const prod = {
  id: "next-dapp",
  html: {
    title: "Next Dapp | The Bridge between Web 2.0 and 3.0",
    description:
      "Next Dapp is a web framework to progressively connect web 2.0 with 3.0.",
    image: "https://picsum.photos/1000/500",
    "theme-color": "#03414D"
  },
  firebase: {
    name: "xxx-xxx",
    id: "xxx-xxx",
    key: "xxxxxxx",
    sender: "123456789",
    region: "us-central1"
  },
  web3: {
    network: "3" // Ropsten Testnet
  },
  alis: {
    client_id: "123456789,
    redirect_uri: "http://localhost:3000/"
  },
  steem: {
    redirect_uri: "http://localhost:3000/",
    app: "ocrybit"
  },
  uport: {
    name_field: "氏名又は名称",
    id_field: "交付番号",
    appName: "暗号資産古物商協会",
    did: "did:ethr:0x62507aa089182659ff595266e3c1de2975a51780",
    verified: "古物商許可証テストネット"
  }
}

module.exports = mergeAll([prod, local])

4. Setup credentials as env values

Sensitive data such as private keys are not to be mixed with the configurations above, use .env for credintials.

touch .env

replace below with your data [/.env]

UPort requires my private key and not configurable for anyone other than me at the moment.

# if not used, you don't need alis and uport credentials
ALIS_CLIENT_SECRET=xxxxxxxxx
UPORT_PRIVATE_KEY=xxxxxxxx
WEB3_INFURA=https://ropsten.infura.io/v3/xxxxxxx
UPORT_RPCURL=https://mainnet.infura.io/v3/xxxxxxxx

5. Setup Firestore and Cloud Functions

Initialize firebase project (choose Firestore and Functions)

Create a new project if you don't have one to use for the development. Do not overwrite any files during the initialization such as firebase/firestore.rules. If you keep pressing Enter without changing the default options, you won't overwrite anything.

Alternatively, create one from the web console https://console.firebase.google.com. Then run below.

You need to set a cloud resource location by setting up Firestore via the web console above.

You need to install firebase-tools and set it up first. Refer to this page.

npm install -g firebase-tools

Once you set it up and log in, proceed.

cd firebase && firebase init

How to set up login methods

  • Google, Twitter, Github, Facebook can be set up via Firebse console.
  • ALIS: Register an OAuth app from here, and set the redirect URI to http://localhost:3000/.
  • STEEM: Set up steemconnect somehow... I forgot how :)
  • Metamask, Authereum: You will need the @nextdapp/web3 plugin to make it work.
  • UPort: It only works with my private key at the moment, to be extended.

update security rules for Firestore

firebase deploy --only firestore:rules

create env.json and deploy environmental variables and functions

Functions are only compatible with node v.10.x. Use nvm to temporarily switch versions.

nvm can be installed by yarn or npm

yarn global add nvm

Create /firebase/functions/env.json and modify as shown below. If you are not using the web3 based login methos, just copy and paste below for dummy data.

cd functions && nvm use 10 && yarn && touch env.json

/firebase/functions/env.json


{
  "web3": {
    "infura": "https://ropsten.infura.io/v3/xxxxx"
  },
  "uport": {
    "name_field": "氏名又は名称",
    "id_field": "交付番号",
    "appName": "暗号資産古物商協会",
    "did": "did:ethr:0x62507aa089182659ff595266e3c1de2975a51780",
    "privateKey":
      "xxxxxxx",
    "rpcUrl": "https://mainnet.infura.io/v3/xxxxx",
    "verified": "古物商許可証テストネット"
  }
}

Upload env to your remote Cloud Functions environment. This command will do the magic.

firebase functions:config:set env="$(cat env.json)"

Deploy functions.

cd ..
firebase deploy --only functions

6. Run the demo locally

cd ../demo
yarn dev

Adding a New Login Method

1. add login mehtod data to const.js

/src/const.js

Example: url is optional.

{
  key: "alis",
  name: "ALIS",
  bg: "#232538",
  url: v => `https://alis.to/users/${v.username}`
}

2. add two types of icons to /static/images directory

Name them github-white.png(Login Button) and github.png(Link to User Account).

3. add a function to const login_with in /src/account.js

Example

This will redirect the user to alis.to for an OAuth2.0 authentication flow.

You automatically get { set, values, conf } as the arguments.

  alis: async ({ conf }) => {
    const code_verifier = get_code_verifier()
    const code_challenge = get_code_challenge(code_verifier)
    const purl = `https://alis.to/oauth-authenticate?client_id=${
      conf.alis.client_id
    }&redirect_uri=${encodeURIComponent(
      conf.alis.redirect_uri
    )}&scope=write&code_challenge=${code_challenge}`
    setCookie(null, "alis_verifier", code_verifier, { path: "/" })
    window.location.href = purl
  },

4. add a callback function to execute after redirecting back from OAuth provider (if needed)

Next-Dapp utilizes Recoil - the Facebook official state management library.

If you change global states, you need to list them in the first argument like done below -["processing$util"].

set can change globally reactive states in this way.

If you need your own global states, define them in /src/init.js, and add them to /nextdapp.json. If you define your_state, it will namespaced and become your_state$account throughout the app.

Example

export const check_alis = [
  ["processing$util"],
  async ({ val: { router }, state$, set }) => {
    const code = router.query.code
    if (isNil(code)) return
    set(true, "processing$util")
    const cookies = parseCookies()
    const alis_verifier = cookies.alis_verifier
    let user = await checkUser(state$)
    let login_url = `/api/alis-oauth_account?code=${code}&verifier=${
      cookies.alis_verifier
    }`
    await _login_with({ login_url, set, state$, provider: "alis" })
    router.replace(router.pathname, router.pathname, { shallow: true })
  }
]

5. set a user data converter via const name_map and link_converter

Example

Pick necessary fields to form a user data from add.

name, image and description are preffered to be assigned.

const name_map = {
  "alis.to": "alis",

const link_converter = {
  "alis.to": (_u, add) =>
    mergeLeft(
      {
        name: add.user_display_name,
        image: add.icon_image_url,
        description: add.self_introduction,
        username: add.user_id
      },
      _u
    ),

6. bind and execute the callback function when the required query parameter exists in the demo.

Plugin functions will be automatically namespaced. check_alis becomes fn.check_alis$account.

Example: /demo/page/index.js

  useEffect(
    () => {
      if (hasPath(["router", "query", "code"])(props)) {
        isFirebase(props.conf).then(() => {
          fn.check_alis$account({ router: props.router })
        })
      }
    },
    [props.router.query]
  )

Don't forget to bind the function to the Component.

  [
    "user$account",
    "uport$account",
    "processing$util",

    "changeUser$account",
    "check_alis$account", // <= bind to Component
    "linkAccount$account",
    "unlinkAccount$account"
  ]

7. write the logic to generate custom_token using Next.js api or/and Cloud Functions.

For alis, refer to /src/api/alis-oauth.js and exports login in /firebase/functions/index.js.

If you need any credentials to set for Cloud Functions, refer back to create env.json and deploy environmental variables and functions

Public configurations for Next.js app can be set as described in 2. Copy and Modify conf.sample.js file

Credentials for Next.js app can be set as described in 4. Setup credentials as env values

8. if you have added public apis, cloud functions or env variables, specify them in /nextdapp.js

{
  "epics": [ "changeUser", "logout", "logout_authereum", "login", "check_alis", "deleteAccount", "linkAccount", "unlinkAccount" ],
  "props": [ "user", "user_init", "uport", "_user" ],
  "api": { "apiAlisOauth": "alis-oauth", "apiSteemOauth": "steem-oauth", "apiUportLogin": "uport-login" },
  "functions": { "nd-account-functions": [ "loginUport", "loginMetamask", "loginAuthereum", "login" ] },
  "env": [ "ALIS_CLIENT_SECRET", "UPORT_PRIVATE_KEY", "WEB3_INFURA", "UPORT_RPCURL" ],
  "components": ["Login", "UPort", "Profile", "LinkAccount"]
}

9. build pulugin, update package and deploy functions

build plugin

yarn build

move to demo directory and update @nextdapp/account package

Use .. noinstall for local rebuild and update. It will link to the updated local file without installing the node package.

You don't need them when updating directly from node package registry.

cd demo
nextdapp remove @nextdapp/account .. noinstall
nextdapp add @nextdapp/account .. noinstall