5
0
Fork 0

Initial commit

master
UnicodingUnicorn 2019-02-22 23:08:10 +08:00
parent e185b09d17
commit 3879bbb3e6
5 changed files with 103 additions and 15 deletions

2
.env Normal file
View File

@ -0,0 +1,2 @@
LISTEN=127.0.0.1:3000
SECRET=secret

18
.gitignore vendored
View File

@ -1,14 +1,4 @@
# ---> Go
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# ---> Rust
/target
Cargo.lock
**/*.rs.bk

14
Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "backend-auth"
version = "0.1.0"
authors = ["UnicodingUnicorn <7555ic@gmail.com>"]
edition = "2018"
[dependencies]
dotenv = "0.13.0"
lazy_static = "1.2.0"
iron = "0.6.0"
router = "0.6.0"
jsonwebtoken = "5"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"

View File

@ -1,3 +1,14 @@
# backend-auth
Beep backend auth proxy
Beep backend auth proxy. At long last, something done properly in Rust. My ancestors are smiling at me, Imperial, can you say the same?
Is basically tailored just for traefik's Forward Authentication system. It takes a `GET`, `POST`, `PUT`, `PATCH` or `DELETE` request, reads a Bearer Auth JWT token if available. If it is not available or invalid, request fails with 4XX and traefik rejects the request. Otherwise, a success response is returned with a `X-User-Claim` header containing serialised user information. `OPTIONS` requests are allowed to pass through wholesale.
## Contents of `X-User-Claim`
```json
{
"userid": "<userid>",
"clientid": "<clientid>"
}
```

71
src/main.rs Normal file
View File

@ -0,0 +1,71 @@
#[macro_use]
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate dotenv;
extern crate iron;
extern crate router;
extern crate jsonwebtoken as jwt;
use dotenv::dotenv;
use iron::prelude::*;
use iron::headers::{ Authorization, Bearer };
use iron::status::Status;
use router::Router;
use std::env;
#[derive(Debug, Serialize, Deserialize)]
struct UserClaims {
userid: String,
clientid: String,
}
fn main() {
dotenv().ok();
let listen = env::var("LISTEN").unwrap();
lazy_static! {
static ref secret:String = env::var("SECRET").unwrap();
}
fn verify_jwt(req:&mut Request) -> IronResult<Response> {
if let Some(authorisation_header) = req.headers.get::<Authorization<Bearer>>() {
match jwt::decode::<UserClaims>(&authorisation_header.token, secret.as_ref(), &jwt::Validation{ validate_exp:false, ..Default::default()}) { // Don't validate expiry
// match jwt::decode::<UserClaims>(&authorisation_header.token, secret.as_ref(), &jwt::Validation::default()) { // Production version
Ok(decoded) => {
let user_string = match serde_json::to_string(&decoded.claims) {
Ok(s) => s,
Err(_) => return Ok(Response::with((Status::Unauthorized, "401 Unauthorised"))),
};
let mut res = Response::with((Status::Ok, "200 Ok"));
res.headers.set_raw("X-User-Claim", vec![user_string.as_bytes().to_vec()]);
Ok(res)
},
Err(_) => Ok(Response::with((Status::Unauthorized, "401 Unauthorised")))
}
} else {
Ok(Response::with((Status::BadRequest, "400 Bad Request")))
}
}
let mut router = Router::new();
// Let OPTIONS through
router.options("/auth", success, "auth_options");
// Auth GET, POST, PUT, PATCH, DELETE
router.get("/auth", verify_jwt, "auth_get");
router.post("/auth", verify_jwt, "auth_post");
router.put("/auth", verify_jwt, "auth_put");
router.patch("/auth", verify_jwt, "auth_patch");
router.delete("/auth", verify_jwt, "auth_delete");
match Iron::new(router).http(&listen) {
Ok(_) => println!("Listening on {}", &listen),
Err(e) => println!("Error: {:?}", e),
};
}
fn success(_:&mut Request) -> IronResult<Response> {
Ok(Response::with((Status::Ok, "")))
}