Git Product home page Git Product logo

node-eauth-server's Introduction

Eauth Server · GitHub license

Introduction

An OAuth2-compatible service based on Ethereum credentials to authenticate users on website.

And also has these features:

Demo

Eauth - An Oauth2 compatible authentication service: https://www.youtube.com/watch?v=fE5B7DaRHnA

Hakka Forum with Eauth: https://forum.hakka.finance

Usages: eauth-examples

Requirements

  • Node.js 10 or higher

Installation

1. Clone this repo, and install dependencies.

Using yarn:

$ yarn install

Notice: For those who are not using SQLite:

$ yarn install --ignore-optional

2. Configure Eauth config.

2.1. Copy an example configuration:

$ cp .env.example .env

2.2. Configure your .env following .env.example:

See more information : Sequelize configuration

3. Setup OAuth Clients.

Manual

3.1.1 Connect to your database, and fulfill the table below with Oauth data

Table: oauth_clients

client_id client_secret redirect_uri
... ... ...

Insert a client config when the server starts

3.2.1 Setup your client configs name, client_id, client_secret, redirect_uri in .env

Usage

Quickstart

Start the server: node -r dotenv/config index.js.
Test it on http://localhost:8080/.

Using PM2

$ npm i -g pm2

$ cp pm2.config.js.example pm2.config.js

$ pm2 start pm2.config.js --env development // development mode on port 8080

// or

$ pm2 start pm2.config.js --env production // production mode on port 80

Docker

Get it from DockerHub

For normal version

$ docker pull pelith/node-eauth-server:latest
$ docker run --net=host --env-file ./.env -d pelith/node-eauth-server

For ENS version

$ docker pull pelith/node-eauth-server:latest-ENS
$ docker run --net=host --env-file ./.env -d pelith/node-eauth-server

Optionally: Build docker image manually

$ docker build -t pelith/node-eauth-server .

Tutorial

This service requires a wallet which supports eth_signTypedData, personal_sign or customized method for your contract wallet. For first-time visitors, the simplest setup is to include a MetaMask download badge before proceeding to the authentication page.

Browser Extensions (MetaMask) Mobile Wallets (imToken / Trustwallet) Other SDK (Fortmatic)
MetaMask badge imToken badge Trustwallet badge Fortmatic badge
  1. In the page /, you can decide to login with your Ethereum wallet or contract wallet which implements ERC-1271.

    Main Page

  2. For Ethereum wallet, there is no email/id/password input fields. Instead, you gotta sign in with your Ethereum credentials. If your MetaMask is locked or in the privacy mode, it would prompt you to unlock. You can also scan the QR Code to open the URL with your mobile wallet (imToken or Trustwallet), then sign the message for authentication through socket.

    Login with Ethereum

  3. In your wallet, you should check the banner and the prefix of message, usually the brand name of a site. The challenge message should contain a token string. If it's the correct info from the site you are about to login, click "Sign" or "Confirm" to proceed.

    Signing Process

  4. Next, your wallet address is shown and you are asked for authorization. This step is to bind that wallet address to your account. Click "Authorize" to proceed, or click "Use another account" if this is not the account you intend to use.

    Authorise

  5. If everything is fine, you will be redirected back to the original site. Clicking "Logout" will log you out and reset the session.

  6. For contract wallet, you'll have to input your contract address (ENS is also acceptable if the feature is enabled), Click "Use Contract" and choose your way to verify.

    Contract Input

  7. The eth_signTypedData and personal_sign will both works if you implement the ERC-1271 like this. The signing process will be the same as Ethereum login. However, if you're using customized signature for verification, click "Customized Sign".

    Contract

  8. For Customized Sign, server will return the full message for signing and the hexed message after web3.sha3(message). Sign the message with your customized way and fill the signature below. Click "Verify Signature" to login with your contract wallet.

    Customized

Discourse Integration

  1. Install discourse-eauth plugin by following this guide.

  2. Enable the plugin at /admin/site_settings/category/plugins. Setup Plugin Configs

  3. Set max username length up to 42. Remember to setup username change period if you're allowing users to edit their username instead of using the address they registered. username length edit username

  4. Setup OAuth client and use http://your.domain/auth/eauthoauth2/callback as your OAuth redirect_uri

  5. Finally, enjoy!

Fortmatic

Let users access blockchain apps from anywhere 💻📱 - without forcing them to wrestle with browser extensions, wallets, or seed phrases, see more at fortmatic.com

License

Node Eauth Server is MIT licensed.

node-eauth-server's People

Contributors

andy0130tw avatar artistic709 avatar dependabot[bot] avatar gilg4mesh avatar hung-pelith avatar rht avatar siriustsou avatar xiawpohr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-eauth-server's Issues

The Docker image doesn't work out of the box

These are the steps I had to do to make the Eauth on Docker work:

  • Did docker-compose up -d
  • Since the seed inside the node-eauth-server container is not set up, I had to
    modify components/seeders/20190725062038-oauth_clients.js to contain my
    client_id, client_secret, and redirect_uri
  • Did npx sequelize-cli db:seed:all inside the container, because npx sequelize db:seed:all
    (without -cli) said "command not found". But this resulted in ERROR: Cannot find "/usr/src/app/config/config.json". Have you run "sequelize init"?, so I
    did npx sequelize-cli init instead
  • npx sequelize-cli init inside the container generated a sample config/config.js file, but this
    needs to be manually configured with my MySQL username, password, database
    name, host, which I already provided in the eauth-vars.env file above.
    Note that I don't actually have to run the sequelize-cli init; I just need
    to prepare the config/config.js
  • Reran npx sequelize-cli db:seed:all inside the container. This resulted and error because the
    table oauth_clients doesn't exist. We fix that in the next step.
  • Ran docker exec -it my_container pkill node. This restarted the node
    server, and triggered the DB tables creation.
  • Ran npx sequelize-cli db:seed:all inside the container

After these steps, the Eauth is authenticating smoothly.

This is my relevant docker-compose.yml config:

  eauth:
    image: pelith/node-eauth-server:latest
    restart: always
    env_file:
      - eauth-vars.env
    ports:
      - 8080:8080
    links:
      - database
    volumes:
      - ./aqua/eauth_seed.js:/usr/src/app/components/seeders/20190725062038-oauth_clients.js
      - ./aqua/eauth_config.json:/usr/src/app/config/config.json
    depends_on:
      - database

Notice that I mounted the seed js and the config/config.json to automate the editing of the files. But ideally these could be made as parameters that are fed into the container.

cc: @hung-pelith

An LGPLv3 code is accidentally vendored in the commit "update: use js ans css from local"

Walletconnect (https://www.npmjs.com/package/walletconnect) is LGPLv3 licensed, and so if you want to republish it, you have to do it under the same license or GPLv3.

IANAL, but according to https://softwareengineering.stackexchange.com/questions/181260/should-i-rewrite-the-git-history-of-a-non-gpl-project-to-remove-gpl-code-that-wa, you have to rewrite the git history to remove the commit.

Also, IMO, the public/script content should be auto-generated by an npm run build or something, and put into .gitignore.

[Usability] Link-through to specific wallet provider to decrease login times

image

We would like to 'quick' link directly to the 'Metamask' Wallet for login to skip 3 clicks and wait times.
Best would be if there is a 'deterministic link' which allows to target all the different selection options, so we can link into that stage of the selection wizard.

This will help to decrease login times and user-experience.

Fields with spaces in dotenv

Dotenv files commonly contain string literals with spaces due to bash field splitting. The typical format of an env file to prevent bash field splitting is to wrap the string with spaces like:

EAUTH_BANNER="Some Long String"
EAUTH_MESSAGE_PREFIX="Authenticate with your MetaMask wallet" 

In contract.js the above format causes issues when reading with process.env as the result is "Some long string" and not Some long string. I propose to add some handling logic in contract.js to handle this common case for better interoperability with bash scripts.

.env.example should contain comments as found in README.md

E.g.

# your brand name
EAUTH_BANNER=YOUR_BANNER_HERE
# morgan logger
EAUTH_LOGGING=true

This is so that users can see the context for the variables on the spot, inside the .env.example file.
To avoid duplication, maybe the config text in README.md should be removed instead.

Failed login on recent Chrome-Release but working with older Brave (Chromium) version

Tested on:

  • Fails on Chrome Version 94.0.4606.61 (Official Build) (64-bit)
  • Working on Brave (Chromium) Version 1.29.81 Chromium: 93.0.4577.82 (Official Build) (64-bit)

browser logs (failed one):
image

docker container logs:

socket disconnect
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `data`=?,`expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `data`=?,`expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `data`=?,`expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?
Executing (default): SELECT `sid`, `data`, `expires` FROM `session` AS `Session` WHERE `Session`.`sid` = 'WYlF1qW6yQ50YWDf70Jxt3E0G_7zMsw7';
Executing (default): UPDATE `session` SET `expires`=? WHERE `sid` = ?

[Offline Capability] Currently the container seems not to work if connection is lost.

We require the eauth container service to run fully offline if required. Currently it fails to work if connection is disrupted. What information are exchanged and required to be exchanged? To increase trust in the service we would like to ensure that it can run fully off-line for local-offline setups or for other environments which do not trust the eauth server to be online for a deployment.

image

Add option to use Sequelize Sqlite3 backend

This is useful if people want to try to test-deploy node-eauth-server and experiment with the UX with minimal installation overhead.

I can make a PR for this if you think it's worth implementing.

Typo: compatiable

An OAuth-compatiable service based on Ethereum credentials to authenticate users on a website

should be "compatible".

Same typo in the graphic near the bottom of https://eauth.app

env vars: Client details should not be optional

In current master, the check for whether the DB is seeded is if (process.env.EAUTH_CLIENT_NAME && process.env.EAUTH_CLIENT_ID && process.env.EAUTH_CLIENT_SECRET && process.env.EAUTH_REDIRECT_URI). Which doesn't seed if even one of the env vars is not specified. Those vars shouldn't be optional, and so users know that they have to specify them. This is especially the case when users are upgrading from the older version of the Docker container.

Sequelize CLI: Cannot find `config/config.json`

Hi, I'm trying to setup a NodeJS EAUTH server as per your README instructions, I'm stuck at:

~/eauth user@errai
❯ npx sequelize db:seed:all

Sequelize CLI [Node: 16.10.0, CLI: 5.5.1, ORM: 5.22.3]


ERROR: Cannot find "/home/user/eauth/config/config.json". Have you run "sequelize init"?

~/eauth user@errai
❯ npx sequelize init       

Sequelize CLI [Node: 16.10.0, CLI: 5.5.1, ORM: 5.22.3]

Created "config/config.json"
models folder at "/home/user/eauth/models" already exists.

ERROR: The file /home/user/eauth/models/index.js already exists. Run command with --force to overwrite it.

Running npx sequelize init --force is not an option though. What's missing?

MediaWiki OAuth2 Client: Server error: `accessTokenExpiresAt` must be a Date instance

Thank you for building such a featureful Ethereum provider!

I'm trying to set up eauth as a provider for my MediaWiki instance, using: https://www.mediawiki.org/wiki/Extension:OAuth2_Client.

Relevant configuration on the LocalSettings.php


$wgOAuth2Client['configuration']['authorize_endpoint']     = 'http://localhost:8080/oauth/authorize'; // Authorization URL
$wgOAuth2Client['configuration']['access_token_endpoint']  = 'http://localhost:8080/oauth/token'; // Token URL
$wgOAuth2Client['configuration']['api_endpoint']           = 'http://localhost:8080/oauth/user'; // URL to fetch user JSON
$wgOAuth2Client['configuration']['redirect_uri']           = 'http://localhost:9352/index.php/Special:OAuth2Client/callback'; // URL for OAuth2 server to redirect to

$wgOAuth2Client['configuration']['username'] = 'address'; // JSON path to username

I tried to do the signing with the metamask, but I got error during the GET for /oauth/user with this error:

server_error: Server error: `accessTokenExpiresAt` must be a Date instance
    at new ServerError (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/oauth2-server/lib/errors/server-error.js:25:14)
    at AuthenticateHandler.validateAccessToken (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/oauth2-server/lib/handlers/authenticate-handler.js:220:11)
    at AuthenticateHandler.<anonymous> (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/oauth2-server/lib/handlers/authenticate-handler.js:74:19)
    at PassThroughHandlerContext.finallyHandler (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/finally.js:57:23)
    at PassThroughHandlerContext.tryCatcher (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/promise.js:547:31)
    at Promise._settlePromise (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/promise.js:604:18)
    at Promise._settlePromise0 (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/promise.js:649:10)
    at Promise._settlePromises (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/promise.js:729:18)
    at _drainQueueStep (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/async.js:93:12)
    at _drainQueue (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/async.js:86:9)
    at Async._drainQueues (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/async.js:102:5)
    at Immediate.Async.drainQueues [as _onImmediate] (/var/www/html/extensions/DataAccounting/node-eauth-server/node_modules/bluebird/js/release/async.js:15:14)
    at processImmediate (node:internal/timers:464:21) {
  statusCode: 503,
  status: 503,
  code: 503
}

Is this because I am using sqlite as the sequelize dialect, and so the date is not initialized properly? Or is it a problem with the MediaWiki OAuth2 client?

For extra information, here is the output of the access token select

sqlite> SELECT `OAuthAccessToken`.`id`, `OAuthAccessToken`.`access_token` AS `accessToken`, `OAuthAccessToken`.`expires` AS `accessTokenExpiresAt`, `OAuthAccessToken`.`scope`, `User`.`id` AS `User.id`, `User`.`address` AS `User.address`, `OAuthClient`.`id` AS `OAuthClient.id`, `OAuthClient`.`name` AS `OAuthClient.name`, `OAuthClient`.`client_id` AS `OAuthClient.client_id`, `OAuthClient`.`client_secret` AS `OAuthClient.client_secret`, `OAuthClient`.`redirect_uri` AS `OAuthClient.redirect_uri`, `OAuthClient`.`grant_types` AS `OAuthClient.grant_types`, `OAuthClient`.`scope` AS `OAuthClient.scope`, `OAuthClient`.`user_id` AS `OAuthClient.user_id` FROM `oauth_access_tokens` AS `OAuthAccessToken` LEFT OUTER JOIN `user` AS `User` ON `OAuthAccessToken`.`user_id` = `User`.`id` LEFT OUTER JOIN `oauth_clients` AS `OAuthClient` ON `OAuthAccessToken`.`client_id` = `OAuthClient`.`id` WHERE `OAuthAccessToken`.`access_token` = 'c0a0497503f64f5e68a3500a55f9d2063668db54' LIMIT 1;
1|c0a0497503f64f5e68a3500a55f9d2063668db54|2021-08-29 10:29:13.724 +00:00|openid email profile|1|0xREDACTEDOFCOURSE|1||pkc|pkc|http://localhost:9352/index.php/Special:OAuth2Client/callback|||

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.