Skip to content

Commit

Permalink
io.jwt.decode (microsoft#140)
Browse files Browse the repository at this point in the history
* io.jwt.decode

Signed-off-by: Anand Krishnamoorthi <[email protected]>

* Update README

Signed-off-by: Anand Krishnamoorthi <[email protected]>

* Install musl-tools to compile ring crate

ring crate is a dependency of jsonwebtoken

Signed-off-by: Anand Krishnamoorthi <[email protected]>

---------

Signed-off-by: Anand Krishnamoorthi <[email protected]>
  • Loading branch information
anakrish authored Feb 10, 2024
1 parent a381c38 commit 3b2e639
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:
- uses: actions/checkout@v3
- name: Add musl target
run: rustup target add x86_64-unknown-linux-musl
- name: Install musl-gcc
run: sudo apt update && sudo apt install -y musl-tools
- name: Format Check
run: cargo fmt --check
- name: Build
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ crypto = ["dep:constant_time_eq", "dep:hmac", "dep:hex", "dep:md-5", "dep:sha1",
deprecated = []
hex = ["dep:data-encoding"]
http = []
jwt = []
jwt = ["dep:jsonwebtoken", "dep:data-encoding"]
glob = ["dep:wax"]
graph = []
jsonschema = ["dep:jsonschema"]
Expand Down Expand Up @@ -91,7 +91,8 @@ jsonschema = { version = "0.17.1", default-features = false, optional = true }
chrono = { version = "0.4.31", optional = true }
chrono-tz = { version = "0.8.5", optional = true }
compact-rc = "0.5.2"

jsonwebtoken = { version = "9.2.0", optional = true }
itertools = "0.12.1"

[dev-dependencies]
clap = { version = "4.4.7", features = ["derive"] }
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ The following test suites don't pass fully due to mising builtins:
- `graphql`
- `invalidkeyerror`
- `jsonpatch`
- `jwtbuiltins`
- `jwtdecodeverify`
- `jwtencodesign`
- `jwtencodesignraw`
Expand Down
49 changes: 47 additions & 2 deletions src/builtins/jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,64 @@

use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::builtins::utils::ensure_args_count;
use crate::builtins::utils::{ensure_args_count, ensure_string};

use crate::lexer::Span;
use crate::value::Value;

use itertools::Itertools;
use std::collections::HashMap;

use anyhow::Result;
use anyhow::{bail, Result};

pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("io.jwt.decode", (jwt_decode, 1));
m.insert("io.jwt.decode_verify", (jwt_decode_verify, 2));
}

fn decode(span: &Span, jwt: String, strict: bool) -> Result<Value> {
let Some((Ok(header), Ok(payload), Ok(signature))) = jwt
.split('.')
.map(|p| data_encoding::BASE64URL_NOPAD.decode(p.as_bytes()))
.collect_tuple()
else {
if strict {
bail!(span.error("invalid jwt token"));
}
return Ok(Value::Undefined);
};

let header = String::from_utf8_lossy(&header).to_string();
let payload = String::from_utf8_lossy(&payload).to_string();
let signature = data_encoding::HEXLOWER_PERMISSIVE.encode(&signature);

let signature = Value::String(signature.into());
let header = Value::from_json_str(&header)?;

if header["enc"] != Value::Undefined {
bail!(span.error("JWT is a JWE object, which is not supported"));
}

if header["cty"] == "JWT".into() {
if payload.len() <= 2 || !payload.starts_with('"') || !payload.ends_with('"') {
bail!(span.error("invalid nested JWT"));
}
// Ignore ""
decode(span, payload[1..payload.len() - 1].to_string(), strict)
} else {
let payload = Value::from_json_str(&payload)?;
Ok(Value::from_array([header, payload, signature].into()))
}
}

fn jwt_decode(span: &Span, params: &[Ref<Expr>], args: &[Value], strict: bool) -> Result<Value> {
let name = "io.jwt.decode";
ensure_args_count(span, name, params, args, 1)?;
let jwt = ensure_string(name, &params[0], &args[0])?;

decode(span, jwt.to_string(), strict) //header, payload, signature, strict)
}

fn jwt_decode_verify(
span: &Span,
params: &[Ref<Expr>],
Expand Down
3 changes: 1 addition & 2 deletions tests/opa.passing
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ jsonfilteridempotent
jsonremove
jsonremoveidempotent
jsonschema
jwtencodesignheadererrors
jwtencodesignpayloaderrors
jwtbuiltins
negation
nestedreferences
numbersrange
Expand Down

0 comments on commit 3b2e639

Please sign in to comment.