Giter Club home page Giter Club logo

canduma's Introduction

MIT license Status Status

Canduma rust Graphql

A Rust authentication server with GraphQL API, Diesel, PostgreSQL session authentication and JWT

This repository contains a GraphQL server with JWT up and running quickly.

It uses actix-web, Juniper, Diesel and jsonwebtoken

Your own pull requests are welcome!

Benchmarks with insert into PostgreSQL

▶ ./bombardier -c 125 -n 10000000 http://localhost:3000/graphql -k -f body --method=POST -H "Content-Type: application/json" -s
Bombarding http://localhost:3000/graphql with 10000000 request(s) using 125 connection(s)

10000000 / 10000000 [===========================================================================] 100.00% 28777/s 5m47s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     28788.66    2183.47   34605.95
  Latency        4.32ms   543.07us   110.95ms
  HTTP codes:
    1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:    20.75MB/s

Collection of major crates used in Canduma

Required

  • Rustup
  • Stable Toolchain: rustup default stable
  • Diesel cli with postgres cargo install diesel_cli --no-default-features --features "postgres"
  • PostgreSQL database server or use our docker-compose.yml (require docker)

Getting Started

git clone https://github.com/clifinger/canduma.git
cd canduma
docker-compose up
cp .env.example .env
diesel setup --database-url='postgres://postgres:canduma@localhost/canduma'
diesel migration run
cargo run

Test the GraphQL API with Insomnia

Register

Register with Insomnia

Login

Login with Insomnia

Get my account

Login with Insomnia

Get JWT Token

Get JWT by GraphQL with Insomnia

Set Bearer JWT Token

Set JWT Token with Insomnia

Get decoded JWT by the server (for tests purpose)

Get JWT decoded Token by GraphQL with Insomnia

Test authentication with session in GraphQL by getting all users (for tests purpose)

Get all users by GraphQL with Insomnia

Logout

Logout with Insomnia

Raw code for Insomnia

############ GraphQL Queries ############
query usersQuery {
  users {
    name
    userUuid
    email
    createdAt
  }
}

query tokenQuery {
  token {
    bearer
  }
}

query decodeTokenQuery {
  decode {
    email
    iss
    iat
    exp
    sub
  }
}

Test the GraphQL API with VScode REST Client

VScode plugin

See / open TEST.http file in vscode.

Build release

cargo build --release
cd target/release
./canduma

Security

Important security considerations

We use session cookies for authentication.

Why not JWT authentication?

Stop Using JWT for sessions and why your solution doesn't work

The use of JWT remains secure only if you use adequate storage. This boilerplate is built for use in a micro-services architecture.

JWT can be use for representing claims to be transferred between two parties.

The private key should only be on this micro-service. public key can be used on all other parties to decode the token.

This boilerplate provides a complete example, so we included JWT also.

Generate RSA keys for JWT

In development mode you can keep the one in /keys folder.

// private key
$ openssl genrsa -out rs256-4096-private.rsa 4096

// public key
$ openssl rsa -in rs256-4096-private.rsa -pubout > rs256-4096-public.pem

Logging

Logging controlled by middleware::Logger actix.rs

To enable debug logging set RUST_LOG=debug in .env

Testing

Initialization

First run yarn or npm install to get all required packages

npm run test

To run you can use npm run test or yarn test.

The testing system designed to automatically build canduma offline and start in tests/jest.beforeall.js We starting canduma in order to capture output from both rust and js code using testci target

npm run testci

$ npm run testci

> canduma@ testci /home/olexiyb/b100pro/canduma
> cross-env RUST_LOG=debug DEBUG=canduma:* NODE_ENV=test jest

Determining test suites to run...
$ killall canduma
canduma: no process found

$ cargo build
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
  canduma:jest.beforeall.js build = { status: 0, signal: null, output: [ null, null, null ], pid: 2447, stdout: null, stderr: null } +0ms

$  target/debug/canduma
[2020-04-02T18:17:19Z INFO  actix_server::builder] Starting 24 workers
[2020-04-02T18:17:19Z INFO  actix_server::builder] Starting server on 0.0.0.0:4000
Listening on 0.0.0.0:4000
started API 

  canduma:user.test.js /user/me body='Unauthorized' text="Unauthorized" +0ms

...
[2020-04-02T18:17:22Z DEBUG canduma::user::handler] user_string={"user_uuid":"f7cfa71e-096e-44d0-ae4f-7d16dd9e4baf","email":"[email protected]","role":"bad_role"}
  canduma:user.test.js /graphql body={ data: null, errors: [ { message: 'Unauthorized', locations: [Array], path: [Array], extensions: [Object] } ] } +292ms
 PASS  tests/user.test.js
 
...

In example above you see output from jest tests as well as from rust code debug!("user_string={}", user_string);

CLion

I also highly recommend to use CLion as a dev tool. I allows to run all tests or individual with single click and analyze logs

alt text

canduma's People

Contributors

aminya avatar atul9 avatar clif1414 avatar clifinger avatar dependabot[bot] avatar olexiyb avatar rikusen0335 avatar robjtede avatar s-ted avatar shirshak55 avatar tuxiqae 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

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

canduma's Issues

Authentication inside GraphQL / Juniper

In Node.js GraphQL servers you simply give as context functions that handle the cookies/session to do the authentication, but in this template the authentication is being handled outside of GraphQL, how can I handle it all inside GraphQL? when I try to simply give the actix session stuff it just says it can't be shared between threads.

Connection pooling via r2d2 crate

The project could get a boost in performance by utilizing connection pools with r2d2 and Postgres. This is already flagged as a feature within the cargo.toml for diesel.

This is something most likely I could support with a PR, but wanted to have a discussion for desire before implementing.

Diesel 2.0 released with errors when updating

Diesel updated with version 2.0

Updated Cargo.toml

diesel = { version = "2.0.0", features = ["postgres", "uuid", "chrono", "r2d2"] }

See these errors:

warning: #[table_name] attribute form is deprecated
  = help: use `#[diesel(table_name = users)]` instead

error[E0308]: mismatched types
    --> src/user/service/list.rs:18:23
     |
18   |         .load::<User>(conn)?)
     |          ------------ ^^^^ types differ in mutability
     |          |
     |          arguments to this function are incorrect
     |
     = note: expected mutable reference `&mut _`
                        found reference `&diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>`
note: associated function defined here
    --> /home//.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-2.0.0/src/query_dsl/mod.rs:1497:8
     |
1497 |     fn load<'query, U>(self, conn: &mut Conn) -> QueryResult<Vec<U>>
     |        ^^^^

error[E0308]: mismatched types
    --> src/user/service/login.rs:18:24
     |
18   |         .first::<User>(conn)
     |          ------------- ^^^^ types differ in mutability
     |          |
     |          arguments to this function are incorrect
     |
     = note: expected mutable reference `&mut _`
                        found reference `&diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>`
note: associated function defined here
    --> /home//.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-2.0.0/src/query_dsl/mod.rs:1733:8
     |
1733 |     fn first<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
     |        ^^^^^

error[E0308]: mismatched types
    --> src/user/service/register.rs:16:83
     |
16   |     let inserted_user: User = diesel::insert_into(users).values(&user).get_result(conn)?;
     |                                                                        ---------- ^^^^ types differ in mutability
     |                                                                        |
     |                                                                        arguments to this function are incorrect
     |
     = note: expected mutable reference `&mut _`
                        found reference `&diesel::PgConnection`
note: associated function defined here
    --> /home//.cargo/registry/src/github.com-1ecc6299db9ec823/diesel-2.0.0/src/query_dsl/mod.rs:1677:8
     |
1677 |     fn get_result<'query, U>(self, conn: &mut Conn) -> QueryResult<U>
     |        ^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `canduma` due to 3 previous errors

Suggested way to add new model

How would you suggest adding a new table (struct) to the app? for instance if I wanted each user to have "messages" how would I add that (to be inline with the overall app and API)

Thanks

Get JWT Token, get No

Get JWT Token (http://localhost:3000/graphql), server returns

{
"data": null,
"errors": [
{
"message": "Unauthorized",
"locations": [
{
"line": 14,
"column": 3
}
],
"path": [
"decodeToken"
],
"extensions": {
"type": "NO_ACCESS"
}
}
]
}

Something may be needed when sending Get JWT Token request.

Cannot query field "token" on type "QueryRoot"

I've registered and logged in and have the cookie with auth set, but running this query:

https://github.com/clifinger/canduma/blob/master/docs/images/new-insomnia-grahql-get-jwt.png

I'm getting this result:

{
	"errors": [
		{
			"message": "Unknown field \"token\" on type \"QueryRoot\"",
			"locations": [
				{
					"line": 11,
					"column": 5
				}
			]
		},
		{
			"message": "Unknown field \"decode\" on type \"QueryRoot\"",
			"locations": [
				{
					"line": 17,
					"column": 5
				}
			]
		}
	]
}

Was the schema changed since the getting started steps were made?

discussion: async-graphql vs jupiter and diesel vs tokio-postgres vs sqlx

@clifinger did you know about async-graphql
At this moment this solution has more features and I feel better then use of jupiter.
I personally followed the implementation of twentyfive-stars and liked the interface more than Jupiter. Jupiter supports async, but not released yet and Jupiter lack of good documentation when async-graphql has excellent book

During the investigation, I came across sqlx and found this native rust driver feature to verify SQL during the build is awesome and helps to find bugs before the actual run.

There are actually 3 choices:

I have tried all 3 and finally stopped on sqlx, diesel is crazy complex and not native driver, tokio-postgres is too basic and sqlx is a very good balanced generic solution and I feel performance would be better as it does not have a dependency on libpq (C driver)

I think, that for Bollerplate we need to pick the best solutions on the market and diesel and Jupiter are not the best

Better HTTP Response

Currently response are not all json. For example if we register using same information twice following is the raw data instead of helpful json message like Wrong username or password.

BadRequest: Key (email)=([email protected]) already exists.

[ Feature Request ] support cursor-based pagination

Hi.

Would you like to support cursor-based pagination. So the query will contains nodes, edges, pageInfo, and totalCount.

Such:

{
  hero {
    name
    friends(first:2) {
      totalCount
      edges {
        node {
          name
        }
        cursor
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}

Or something like:

image

Contexts:

Consider Heroku compatibility

I like this project as it's a really easy to use boilerplate for Rust projects. I use Heroku sometimes and also Dokku/Docker so it would be nice to easily use canduma with Heroku instead of building it myself.

Edit: So I was able to use the buildpack at https://github.com/emk/heroku-buildpack-rust to build canduma on Dokku on a DigitalOcean droplet. Since it uses Heroku buildpacks, this should be compatible with Heroku as well. Looks like there may be some bugs with the buildpack on Heroku's buildpack library, so use the full https://github.com/emk/heroku-buildpack-rust url instead when adding the buildpack.

Heroku: heroku buildpacks:set https://github.com/emk/heroku-buildpack-rust.git
Dokku: dokku buildpacks:add https://github.com/emk/heroku-buildpack-rust.git

If you don't set the buildpack as being Rust based, Heroku/Dokku assume it's a NodeJS app due to the package.json in the directory, and the build will fail. Actually, why is there a package.json in the project anyway? Looks like it's used for linting and testing, but I'm not sure what parts are JS based exactly.

Cargo run failed

$ cargo run
info: syncing channel updates for '1.40.0-x86_64-apple-darwin'
info: latest update on 2019-12-19, rust version 1.40.0 (73528e339 2019-12-16)

Compiling ...
Compiling tokio-fs v0.1.6
error: no rules expected the token `#`
   --> /Users/swordsman/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/err-derive-0.2.2/src/lib.rs:138:59
    |
138 | decl_derive!([Error, attributes(error, source, cause)] => #[proc_macro_error] error_derive);
    |                                                           ^ no rules expected this token in macro call

os: macos Catalina 10.15.2
rust: rustc 1.40.0 (73528e339 2019-12-16)

Rust 1.39.0 and Async/Await

Since async/await has landed with Rust 1.39.0, it would be worthwhile to take advantage of the new syntax.

Consider using wundergraph for the database <-> GraphQL interaction

I would like to promote my crate wundergraph. It greatly simplifies the creation of a performant GraphQL over given relational database schema.

Implementations using diesel and juniper in a straight forward most likely have problems with N+1 queries while resolving a nested GraphQL entity. Wundergraph is build on top of diesel and juniper and circumvents this problems carefully craft a fixed number(= independent of the actual number of loaded database entries) of SQL queries to resolve one GraphQL request.
(This seems to be not the case for this example, because there is only one GraphQL entity that is mapped to a database entity, but if this should be a good starting point for a real world application it's probably worth to change it anyway.)

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.