diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e6ed8d..837a3a8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,8 +14,8 @@ jobs: - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 with: - otp-version: "26.0.2" - gleam-version: "1.4.1" + otp-version: "27.2.1" + gleam-version: "1.7.0" rebar3-version: "3" # elixir-version: "1.15.4" - run: gleam deps download diff --git a/gleam.toml b/gleam.toml index f1ded0c..6450798 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,14 +1,14 @@ name = "gwt" -version = "1.0.0" +version = "2.0.0" description = "A JWT library written in Gleam" licences = ["Apache-2.0"] repository = { type = "github", user = "brettkolodny", repo = "gwt" } [dependencies] -gleam_stdlib = "~> 0.34 or ~> 1.0" -gleam_json = "~> 1.0" -gleam_crypto = "~> 1.3" -birl = "~> 1.7" +gleam_stdlib = ">= 0.34.0 and < 2.0.0" +gleam_json = ">= 1.0.0 and < 3.0.0" +gleam_crypto = ">= 1.3.0 and < 2.0.0" +birl = ">= 1.7.0 and < 2.0.0" [dev-dependencies] -gleeunit = "~> 1.0" +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 58de726..d5496fe 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,18 +2,19 @@ # You typically do not need to edit this file packages = [ - { name = "birl", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "B1FA529E7BE3FF12CADF32814AB8EC7294E74CEDEE8CC734505707B929A98985" }, - { name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" }, - { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, - { name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" }, - { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, - { name = "ranger", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "ranger", source = "hex", outer_checksum = "1566C272B1D141B3BBA38B25CB761EF56E312E79EC0E2DFD4D3C19FB0CC1F98C" }, - { name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" }, + { name = "birl", version = "1.8.0", build_tools = ["gleam"], requirements = ["gleam_regexp", "gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "2AC7BA26F998E3DFADDB657148BD5DDFE966958AD4D6D6957DD0D22E5B56C400" }, + { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" }, + { name = "gleam_json", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "C55C5C2B318533A8072D221C5E06E5A75711C129E420DD1CE463342106012E5D" }, + { name = "gleam_regexp", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "A3655FDD288571E90EE9C4009B719FEF59FA16AFCDF3952A76A125AF23CF1592" }, + { name = "gleam_stdlib", version = "0.54.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "723BA61A2BAE8D67406E59DD88CEA1B3C3F266FC8D70F64BE9FEC81B4505B927" }, + { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" }, + { name = "gleeunit", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "0E6C83834BA65EDCAAF4FE4FB94AC697D9262D83E6F58A750D63C9F6C8A9D9FF" }, + { name = "ranger", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_yielder"], otp_app = "ranger", source = "hex", outer_checksum = "C8988E8F8CDBD3E7F4D8F2E663EF76490390899C2B2885A6432E942495B3E854" }, ] [requirements] -birl = { version = "~> 1.7" } -gleam_crypto = { version = "~> 1.3" } -gleam_json = { version = "~> 1.0" } -gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } -gleeunit = { version = "~> 1.0" } +birl = { version = ">= 1.7.0 and < 2.0.0" } +gleam_crypto = { version = ">= 1.3.0 and < 2.0.0" } +gleam_json = { version = ">= 1.0.0 and < 3.0.0" } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/src/gwt.gleam b/src/gwt.gleam index b1654e7..92e0c6f 100644 --- a/src/gwt.gleam +++ b/src/gwt.gleam @@ -4,7 +4,8 @@ import birl import gleam/bit_array import gleam/crypto import gleam/dict.{type Dict} -import gleam/dynamic.{type DecodeError, type Dynamic} +import gleam/dynamic +import gleam/dynamic/decode.{type DecodeError, type Decoder, type Dynamic} import gleam/json.{type Json} import gleam/list import gleam/option.{type Option, None, Some} @@ -27,7 +28,7 @@ pub opaque type JwtBuilder { JwtBuilder(header: Dict(String, Json), payload: Dict(String, Json)) } -/// A decoded JWT that can be read. The phantom type `status` indicated if it's +/// A decoded JWT that can be read. The phantom type `status` indicated if it's /// signature was verified or not. /// pub opaque type Jwt(status) { @@ -82,12 +83,12 @@ pub type Algorithm { // CONSTRUCTORS ---------------------------------------------------------------- -/// Creates a JwtBuilder with an empty payload and a header that only +/// Creates a JwtBuilder with an empty payload and a header that only /// contains the cliams `"typ": "JWT"`, and `"alg": "none"`. /// /// ```gleam /// import gwt.{type Jwt, type Unverified} -/// +/// /// fn example() -> JwtBuilder { /// gwt.new() /// } @@ -104,12 +105,12 @@ pub fn new() -> JwtBuilder { } /// Decode a JWT string into an unverified [Jwt](#Jwt). -/// +/// /// Returns `Ok(JwtBuilder)` if it is a valid JWT, and `Error(JwtDecodeError)` otherwise. /// /// ```gleam /// import gwt.{type Jwt, type Unverified, type JwtDecodeError} -/// +/// /// fn example(jwt_string: String) -> Result(JwtBuilder, JwtDecodeError) { /// gwt.from_string(jwt_string) /// } @@ -123,7 +124,7 @@ pub fn from_string( } /// Decode a signed JWT string into a verified [Jwt](#Jwt). -/// +/// /// Returns `Ok(JwtBuilder)` if it is a valid JWT and the JWT's signature is successfully verified, /// and `Error(JwtDecodeError)` otherwise. /// @@ -132,7 +133,7 @@ pub fn from_string( /// /// ```gleam /// import gwt.{type Jwt, type Verified, type JwtDecodeError} -/// +/// /// fn example(jwt_string: String) -> Result(Jwt(Verified), JwtDecodeError) { /// gwt.from_signed_string(jwt_string, "some secret") /// } @@ -184,22 +185,22 @@ pub fn from_signed_string( /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_iss = +/// let jwt_with_iss = /// gwt.new() /// |> jwt.set_issuer("gleam") -/// +/// /// let assert Ok(issuer) = gwt.get_issuer(jwt_with_iss) -/// +/// /// let jwt_without_iss = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_issuer(jwt_without_iss) /// } /// ``` /// pub fn get_issuer(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { - get_payload_claim(jwt, "iss", dynamic.string) + get_payload_claim(jwt, "iss", decode.string) } /// Retrieve the sub from the JWT's payload. @@ -211,22 +212,22 @@ pub fn get_issuer(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_sub = +/// let jwt_with_sub = /// gwt.new() /// |> jwt.set_subject("gleam") -/// +/// /// let assert Ok(subject) = gwt.get_issuer(jwt_with_sub) -/// +/// /// let jwt_without_sub = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_subject(jwt_without_sub) /// } /// ``` /// pub fn get_subject(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { - get_payload_claim(jwt, "sub", dynamic.string) + get_payload_claim(jwt, "sub", decode.string) } /// Retrieve the aud from the JWT's payload. @@ -238,22 +239,22 @@ pub fn get_subject(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_aud = +/// let jwt_with_aud = /// gwt.new() /// |> jwt.set_audience("gleam") -/// +/// /// let assert Ok(audience) = gwt.get_audience(jwt_with_aud) -/// +/// /// let jwt_without_aud = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_audience(jwt_without_aud) /// } /// ``` /// pub fn get_audience(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { - get_payload_claim(jwt, "aud", dynamic.string) + get_payload_claim(jwt, "aud", decode.string) } /// Retrieve the jti from the JWT's payload. @@ -265,22 +266,22 @@ pub fn get_audience(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_jti = +/// let jwt_with_jti = /// gwt.new() /// |> jwt.set_jwt_id("gleam") -/// +/// /// let assert Ok(jwt_id) = gwt.get_jwt_id(jwt_with_jti) -/// +/// /// let jwt_without_jti = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_jwt_id(jwt_without_jti) /// } /// ``` /// pub fn get_jwt_id(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { - get_payload_claim(jwt, "jti", dynamic.string) + get_payload_claim(jwt, "jti", decode.string) } /// Retrieve the iat from the JWT's payload. @@ -292,22 +293,22 @@ pub fn get_jwt_id(from jwt: Jwt(status)) -> Result(String, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_iat = +/// let jwt_with_iat = /// gwt.new() /// |> jwt.set_issued_at("gleam") -/// +/// /// let assert Ok(issued_at) = gwt.get_issued_at(jwt_with_iat) -/// +/// /// let jwt_without_iat = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_issued_at(jwt_without_iat) /// } /// ``` /// pub fn get_issued_at(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { - get_payload_claim(jwt, "iat", dynamic.int) + get_payload_claim(jwt, "iat", decode.int) } /// Retrieve the nbf from the JWT's payload. @@ -319,22 +320,22 @@ pub fn get_issued_at(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_sub = +/// let jwt_with_sub = /// gwt.new() /// |> jwt.set_not_before("gleam") -/// +/// /// let assert Ok(not_before) = gwt.get_not_before(jwt_with_nbf) -/// +/// /// let jwt_without_nbf = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_not_before(jwt_without_nbf) /// } /// ``` /// pub fn get_not_before(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { - get_payload_claim(jwt, "nbf", dynamic.int) + get_payload_claim(jwt, "nbf", decode.int) } /// Retrieve the exp from the JWT's payload. @@ -346,22 +347,22 @@ pub fn get_not_before(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { /// /// ```gleam /// import gwt -/// +/// /// fn example() { -/// let jwt_with_exp = +/// let jwt_with_exp = /// gwt.new() /// |> jwt.set_not_before("gleam") -/// +/// /// let assert Ok(expiration) = gwt.get_not_before(jwt_with_exp) -/// +/// /// let jwt_without_exp = gwt.new() -/// +/// /// let assert Error(MissingClaim) = gwt.get_not_before(jwt_without_exp) /// } /// ``` /// pub fn get_expiration(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { - get_payload_claim(jwt, "exp", dynamic.int) + get_payload_claim(jwt, "exp", decode.int) } /// Retrieve and decode a claim from a JWT's payload. @@ -371,25 +372,25 @@ pub fn get_expiration(from jwt: Jwt(status)) -> Result(Int, JwtDecodeError) { /// ```gleam /// import gwt /// import gleam/json -/// import gleam/dynamic -/// +/// import gleam/dynamic/decode +/// /// fn example() { /// let jwt_with_custom_claim = /// gwt.new() /// |> gwt.set_payload_claim("gleam", json.string("lucy")) -/// +/// /// let assert Ok("lucy") = -/// gwt.get_payload_claim(jwt_with_custom_claim, "gleam", dynamic.string) -/// +/// gwt.get_payload_claim(jwt_with_custom_claim, "gleam", decode.string) +/// /// let assert Error(MissingClaim) = -/// gwt.get_payload_claim(jwt_with_custom_claim, "gleam", dynamic.int) +/// gwt.get_payload_claim(jwt_with_custom_claim, "gleam", decode.int) /// } /// ``` /// pub fn get_payload_claim( from jwt: Jwt(status), claim claim: String, - decoder decoder: fn(Dynamic) -> Result(a, List(dynamic.DecodeError)), + decoder decoder: Decoder(a), ) -> Result(a, JwtDecodeError) { use claim_value <- result.try( jwt.payload @@ -397,8 +398,7 @@ pub fn get_payload_claim( |> result.replace_error(MissingClaim), ) - claim_value - |> decoder() + decode.run(claim_value, decoder) |> result.map_error(fn(e) { InvalidClaim(e) }) } @@ -406,12 +406,12 @@ pub fn get_payload_claim( /// /// ```gleam /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_issuer("gleam") /// } -/// ``` +/// ``` /// pub fn set_issuer(jwt: JwtBuilder, to iss: String) -> JwtBuilder { let new_payload = dict.insert(jwt.payload, "iss", json.string(iss)) @@ -423,12 +423,12 @@ pub fn set_issuer(jwt: JwtBuilder, to iss: String) -> JwtBuilder { /// /// ```gleam /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_subject("gleam") /// } -/// ``` +/// ``` /// pub fn set_subject(jwt: JwtBuilder, to sub: String) -> JwtBuilder { let payload = dict.insert(jwt.payload, "sub", json.string(sub)) @@ -440,12 +440,12 @@ pub fn set_subject(jwt: JwtBuilder, to sub: String) -> JwtBuilder { /// /// ```gleam /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_audience("gleam") /// } -/// ``` +/// ``` /// pub fn set_audience(jwt: JwtBuilder, to aud: String) -> JwtBuilder { let payload = dict.insert(jwt.payload, "aud", json.string(aud)) @@ -458,14 +458,14 @@ pub fn set_audience(jwt: JwtBuilder, to aud: String) -> JwtBuilder { /// ```gleam /// import gwt /// import birl -/// +/// /// fn example() { /// let five_minutes = birl.to_unix(birl.now()) + 300 -/// +/// /// gwt.new() /// |> gwt.set_expiration(five_minutes) /// } -/// ``` +/// ``` /// pub fn set_expiration(jwt: JwtBuilder, to exp: Int) -> JwtBuilder { let payload = dict.insert(jwt.payload, "exp", json.int(exp)) @@ -478,14 +478,14 @@ pub fn set_expiration(jwt: JwtBuilder, to exp: Int) -> JwtBuilder { /// ```gleam /// import gwt /// import birl -/// +/// /// fn example() { /// let five_minutes = birl.to_unix(birl.now()) + 300 -/// +/// /// gwt.new() /// |> gwt.set_not_before(five_minutes) /// } -/// ``` +/// ``` /// pub fn set_not_before(jwt: JwtBuilder, to nbf: Int) -> JwtBuilder { let payload = dict.insert(jwt.payload, "nbf", json.int(nbf)) @@ -498,12 +498,12 @@ pub fn set_not_before(jwt: JwtBuilder, to nbf: Int) -> JwtBuilder { /// ```gleam /// import gwt /// import birl -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_issued_at(birl.to_unix(birl.now())) /// } -/// ``` +/// ``` /// pub fn set_issued_at(jwt: JwtBuilder, to iat: Int) -> JwtBuilder { let payload = dict.insert(jwt.payload, "iat", json.int(iat)) @@ -516,12 +516,12 @@ pub fn set_issued_at(jwt: JwtBuilder, to iat: Int) -> JwtBuilder { /// ```gleam /// import gwt /// import birl -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_jwt_id("gleam") /// } -/// ``` +/// ``` /// pub fn set_jwt_id(jwt: JwtBuilder, to jti: String) -> JwtBuilder { let payload = dict.insert(jwt.payload, "jti", json.string(jti)) @@ -534,12 +534,12 @@ pub fn set_jwt_id(jwt: JwtBuilder, to jti: String) -> JwtBuilder { /// ```gleam /// import gleam/json /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_payload_claim("gleam", json.string("lucy")) /// } -/// ``` +/// ``` /// pub fn set_payload_claim( jwt: JwtBuilder, @@ -558,12 +558,12 @@ pub fn set_payload_claim( /// ```gleam /// import gleam/json /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_header_claim("gleam", json.string("lucy")) /// } -/// ``` +/// ``` /// pub fn set_header_claim( jwt: JwtBuilder, @@ -583,15 +583,15 @@ pub fn set_header_claim( /// import gwt /// import gleam/json /// import gleam/dynamic -/// +/// /// fn example() { /// let jwt_with_custom_claim = /// gwt.new() /// |> gwt.set_header_claim("gleam", json.string("lucy")) -/// +/// /// let assert Ok("lucy") = /// gwt.get_header_claim(jwt_with_custom_claim, "gleam", dynamic.string) -/// +/// /// let assert Error(MissingClaim) = /// gwt.get_header_claim(jwt_with_custom_claim, "gleam", dynamic.int) /// } @@ -600,7 +600,7 @@ pub fn set_header_claim( pub fn get_header_claim( from jwt: Jwt(status), claim claim: String, - decoder decoder: fn(Dynamic) -> Result(a, List(dynamic.DecodeError)), + decoder decoder: fn(Dynamic) -> Result(a, List(DecodeError)), ) -> Result(a, Nil) { use claim_value <- result.try( jwt.header @@ -609,7 +609,7 @@ pub fn get_header_claim( claim_value |> decoder() - |> result.nil_error() + |> result.replace_error(Nil) } // ENCODER --------------------------------------------------------------------- @@ -618,13 +618,13 @@ pub fn get_header_claim( /// /// ```gleam /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_issuer("gleam") /// |> gwt.to_string() /// } -/// ``` +/// ``` /// pub fn to_string(jwt: JwtBuilder) -> String { let JwtBuilder(header, payload) = jwt @@ -650,13 +650,13 @@ pub fn to_string(jwt: JwtBuilder) -> String { /// /// ```gleam /// import gwt -/// +/// /// fn example() { /// gwt.new() /// |> gwt.set_issuer("gleam") /// |> gwt.to_signed_string(gwt.HS256, "lucy") /// } -/// ``` +/// ``` /// pub fn to_signed_string( jwt: JwtBuilder, @@ -738,9 +738,10 @@ fn parts( ) { let parts = string.split(jwt_string, ".") - use #(encoded_header, parts) <- result.try( - list.pop(parts, fn(_) { True }) |> result.replace_error(MissingHeader), + use encoded_header <- result.try( + list.first(parts) |> result.replace_error(MissingHeader), ) + let parts = list.drop(parts, 1) use header_string <- result.try( encoded_header |> bit_array.base64_url_decode() @@ -748,13 +749,14 @@ fn parts( |> result.replace_error(InvalidHeader), ) use header <- result.try( - json.decode(header_string, dynamic.dict(dynamic.string, dynamic.dynamic)) + json.parse(header_string, decode.dict(decode.string, decode.dynamic)) |> result.replace_error(InvalidHeader), ) - use #(encoded_payload, parts) <- result.try( - list.pop(parts, fn(_) { True }) |> result.replace_error(MissingPayload), + use encoded_payload <- result.try( + list.first(parts) |> result.replace_error(MissingPayload), ) + let parts = list.drop(parts, 1) use payload_string <- result.try( encoded_payload |> bit_array.base64_url_decode() @@ -762,7 +764,7 @@ fn parts( |> result.replace_error(InvalidPayload), ) use payload <- result.try( - json.decode(payload_string, dynamic.dict(dynamic.string, dynamic.dynamic)) + json.parse(payload_string, decode.dict(decode.string, decode.dynamic)) |> result.replace_error(InvalidPayload), ) @@ -783,7 +785,7 @@ fn ensure_valid_expiration( |> result.or(Ok(dynamic.from(-1))) |> result.replace_error(InvalidHeader), ) - dynamic.int(exp) + decode.run(exp, decode.int) |> result.replace_error(InvalidHeader) } use exp <- result.try(exp) @@ -815,7 +817,7 @@ fn ensure_valid_not_before( |> result.or(Ok(dynamic.from(-1))) |> result.replace_error(InvalidHeader), ) - dynamic.int(nbf) + decode.run(nbf, decode.int) |> result.replace_error(InvalidHeader) } use nbf <- result.try(nbf) @@ -847,6 +849,6 @@ fn ensure_valid_alg( ) alg - |> dynamic.string() + |> decode.run(decode.string) |> result.replace_error(InvalidAlg) } diff --git a/test/gwt_test.gleam b/test/gwt_test.gleam index a74d3f6..380b3c1 100644 --- a/test/gwt_test.gleam +++ b/test/gwt_test.gleam @@ -1,5 +1,5 @@ import birl -import gleam/dynamic +import gleam/dynamic/decode import gleam/json import gleeunit import gleeunit/should @@ -32,11 +32,11 @@ pub fn encode_decode_unsigned_jwt_test() { |> should.equal(Ok("1234567890")) jwt - |> gwt.get_payload_claim("aud", dynamic.string) + |> gwt.get_payload_claim("aud", decode.string) |> should.equal(Ok("0987654321")) jwt - |> gwt.get_payload_claim("iss", dynamic.string) + |> gwt.get_payload_claim("iss", decode.string) |> should.equal(Error(gwt.MissingClaim)) } @@ -63,11 +63,11 @@ pub fn encode_decode_signed_jwt_test() { |> should.equal(Ok("1234567890")) jwt - |> gwt.get_payload_claim("aud", dynamic.string) + |> gwt.get_payload_claim("aud", decode.string) |> should.equal(Ok("0987654321")) jwt - |> gwt.get_payload_claim("iss", dynamic.string) + |> gwt.get_payload_claim("iss", decode.string) |> should.equal(Error(gwt.MissingClaim)) let jwt = @@ -150,10 +150,15 @@ pub fn custom_payload_test() { |> gwt.from_string() jwt - |> gwt.get_payload_claim("email", dynamic.string) + |> gwt.get_payload_claim("email", decode.string) |> should.equal(Ok("lucy@gleam.run")) + let data_decoder = { + use age <- decode.field("age", decode.int) + decode.success(age) + } + jwt - |> gwt.get_payload_claim("data", dynamic.field("age", dynamic.int)) + |> gwt.get_payload_claim("data", data_decoder) |> should.equal(Ok(27)) }