Packages

Gleam implementation of JOSE (JWS, JWE, JWK, JWT) and COSE (COSE_Sign, COSE_Encrypt, COSE_Mac0, CWT) standards

Current section

Files

Jump to
gose src gose@encrypted_jwt.erl
Raw

src/gose@encrypted_jwt.erl

-module(gose@encrypted_jwt).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gose/encrypted_jwt.gleam").
-export([key_decryptor/4, password_decryptor/4, encrypt_with_key/4, encrypt_with_password/5, serialize/1, peek_headers/1, decode/2, alg/1, enc/1, kid/1, dangerously_decrypt_and_skip_validation/2, decrypt_and_validate/3]).
-export_type([encrypted_jwt/0, decryptor/0, peek_headers/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Encrypted JWT (JWE-based) - [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519.html)\n"
"\n"
" This module provides encrypted JWT functionality built on top of JWE.\n"
" Encrypted JWTs protect the claims payload through encryption rather than\n"
" just signing, ensuring confidentiality in addition to integrity.\n"
"\n"
" Use `peek_headers()` to inspect a token's headers without decrypting.\n"
" Use `decrypt_and_validate()` to\n"
" decrypt and validate, producing a `EncryptedJwt` whose claims can be\n"
" trusted.\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" import gleam/dynamic/decode\n"
" import gleam/time/duration\n"
" import gleam/time/timestamp\n"
" import gose/encrypted_jwt\n"
" import gose/jwa\n"
" import gose/jwk\n"
" import gose/jwt\n"
"\n"
" let key = jwk.generate_enc_key(jwa.AesGcm(jwa.Aes256))\n"
" let now = timestamp.system_time()\n"
"\n"
" // Create claims and encrypt\n"
" let claims = jwt.claims()\n"
" |> jwt.with_subject(\"user123\")\n"
" |> jwt.with_issuer(\"my-app\")\n"
" |> jwt.with_expiration(timestamp.add(now, duration.hours(1)))\n"
"\n"
" let assert Ok(encrypted) = encrypted_jwt.encrypt_with_key(\n"
" claims, jwa.JweDirect, jwa.AesGcm(jwa.Aes256), key)\n"
" let token = encrypted_jwt.serialize(encrypted)\n"
"\n"
" // Decrypt and validate using Decryptor (enforces algorithm pinning)\n"
" let assert Ok(decryptor) = encrypted_jwt.key_decryptor(\n"
" jwa.JweDirect, jwa.AesGcm(jwa.Aes256), [key], jwt.default_validation())\n"
" let assert Ok(verified) = encrypted_jwt.decrypt_and_validate(decryptor, token, now)\n"
"\n"
" // Decode verified claims\n"
" let decoder = decode.field(\"sub\", decode.string, decode.success)\n"
" let assert Ok(subject) = encrypted_jwt.decode(verified, decoder)\n"
" ```\n"
).
-opaque encrypted_jwt() :: {encrypted_jwt,
gose@jwa:jwe_alg(),
gose@jwa:enc(),
gleam@option:option(binary()),
gose@jwt:claims(),
bitstring(),
binary()}.
-opaque decryptor() :: {key_decryptor,
gose@jwa:jwe_alg(),
gose@jwa:enc(),
list(gose@jwk:jwk()),
gose@jwt:jwt_validation_options()} |
{password_decryptor,
gose@jwa:pbes2_alg(),
gose@jwa:enc(),
binary(),
gose@jwt:jwt_validation_options()}.
-type peek_headers() :: {peek_headers,
gose@jwa:jwe_alg(),
gose@jwa:enc(),
gleam@option:option(binary())}.
-file("src/gose/encrypted_jwt.gleam", 97).
-spec validate_decryption_keys(gose@jwa:jwe_alg(), list(gose@jwk:jwk())) -> {ok,
nil} |
{error, gose:gose_error()}.
validate_decryption_keys(Alg, Keys) ->
gose@internal@key_helpers:require_non_empty_keys(
Keys,
fun() ->
gleam@list:try_each(
Keys,
fun(_capture) ->
gose@internal@key_helpers:validate_key_for_jwe_decryption(
Alg,
_capture
)
end
)
end
).
-file("src/gose/encrypted_jwt.gleam", 124).
?DOC(
" Create a key-based decryptor for symmetric (dir, AES-KW, AES-GCM-KW) or\n"
" asymmetric (RSA-OAEP, ECDH-ES) algorithms.\n"
"\n"
" The decryptor pins the expected algorithms. Tokens with different\n"
" algorithms will be rejected.\n"
"\n"
" ## Parameters\n"
"\n"
" - `alg` - The expected key encryption algorithm.\n"
" - `enc` - The expected content encryption algorithm.\n"
" - `keys` - One or more keys for decryption.\n"
" - `options` - Validation options for JWT claims.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(Decryptor)` configured and ready for use with\n"
" `decrypt_and_validate`, or `Error(JoseError(_))` if the key list is\n"
" empty, any key's `use` field is set but not `Encrypting`, or any key's\n"
" `key_ops` doesn't include `Decrypt` or `UnwrapKey`.\n"
).
-spec key_decryptor(
gose@jwa:jwe_alg(),
gose@jwa:enc(),
list(gose@jwk:jwk()),
gose@jwt:jwt_validation_options()
) -> {ok, decryptor()} | {error, gose@jwt:jwt_error()}.
key_decryptor(Alg, Enc, Keys, Options) ->
_pipe = validate_decryption_keys(Alg, Keys),
_pipe@1 = gleam@result:replace(
_pipe,
{key_decryptor, Alg, Enc, Keys, Options}
),
gleam@result:map_error(_pipe@1, fun(Field@0) -> {jose_error, Field@0} end).
-file("src/gose/encrypted_jwt.gleam", 150).
?DOC(
" Create a password-based decryptor for PBES2 algorithms.\n"
"\n"
" The decryptor pins the expected algorithms. Tokens with different\n"
" algorithms will be rejected.\n"
"\n"
" ## Parameters\n"
"\n"
" - `alg` - The expected PBES2 algorithm.\n"
" - `enc` - The expected content encryption algorithm.\n"
" - `password` - The password for key derivation.\n"
" - `options` - Validation options for JWT claims.\n"
"\n"
" ## Returns\n"
"\n"
" A `Decryptor` configured for use with `decrypt_and_validate`.\n"
).
-spec password_decryptor(
gose@jwa:pbes2_alg(),
gose@jwa:enc(),
binary(),
gose@jwt:jwt_validation_options()
) -> decryptor().
password_decryptor(Alg, Enc, Password, Options) ->
{password_decryptor, Alg, Enc, Password, Options}.
-file("src/gose/encrypted_jwt.gleam", 268).
-spec claims_to_plaintext(gose@jwt:claims()) -> bitstring().
claims_to_plaintext(Claims) ->
_pipe = gose@jwt:claims_to_json_string(Claims),
gleam_stdlib:identity(_pipe).
-file("src/gose/encrypted_jwt.gleam", 191).
-spec do_encrypt_with_key(
gose@jwt:claims(),
gose@jwa:jwe_alg(),
gose@jwa:enc(),
gose@jwk:jwk(),
gleam@option:option(binary())
) -> {ok, encrypted_jwt()} | {error, gose:gose_error()}.
do_encrypt_with_key(Claims, Alg, Enc, Key, Kid) ->
Claims_json = claims_to_plaintext(Claims),
gleam@result:'try'(
gose@internal@key_helpers:validate_key_for_jwe_encryption(Alg, Key),
fun(_) ->
_pipe = gose@jwe:encrypt_to_compact(
Alg,
Enc,
Claims_json,
Key,
Kid,
{some, <<"JWT"/utf8>>},
none
),
gleam@result:map(
_pipe,
fun(Pair) ->
{Token, Jwe_alg} = Pair,
{encrypted_jwt,
Jwe_alg,
Enc,
Kid,
Claims,
Claims_json,
Token}
end
)
end
).
-file("src/gose/encrypted_jwt.gleam", 180).
?DOC(
" Encrypt claims using a key-based algorithm.\n"
"\n"
" Supports all key-based JWE algorithms: direct symmetric (dir), AES Key Wrap,\n"
" AES-GCM Key Wrap, RSA-OAEP, and ECDH-ES. PBES2 password-based algorithms\n"
" return an error — use `encrypt_with_password` instead.\n"
"\n"
" Sets `typ: \"JWT\"` in the header. If the encryption key has a `kid`, it is\n"
" included in the JWE header.\n"
"\n"
" ## Parameters\n"
"\n"
" - `claims` - The JWT claims to encrypt.\n"
" - `alg` - The key encryption algorithm.\n"
" - `enc` - The content encryption algorithm.\n"
" - `key` - The encryption key.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(EncryptedJwt)` with the encrypted JWT ready for\n"
" serialization, or `Error(JoseError(_))` if key validation or encryption\n"
" fails.\n"
).
-spec encrypt_with_key(
gose@jwt:claims(),
gose@jwa:jwe_alg(),
gose@jwa:enc(),
gose@jwk:jwk()
) -> {ok, encrypted_jwt()} | {error, gose@jwt:jwt_error()}.
encrypt_with_key(Claims, Alg, Enc, Key) ->
Kid = gleam@option:from_result(gose@jwk:kid(Key)),
_pipe = do_encrypt_with_key(Claims, Alg, Enc, Key, Kid),
gleam@result:map_error(_pipe, fun(Field@0) -> {jose_error, Field@0} end).
-file("src/gose/encrypted_jwt.gleam", 234).
-spec do_encrypt_with_password(
gose@jwt:claims(),
gose@jwa:pbes2_alg(),
gose@jwa:enc(),
binary(),
gleam@option:option(binary())
) -> {ok, encrypted_jwt()} | {error, gose:gose_error()}.
do_encrypt_with_password(Claims, Alg, Enc, Password, Kid) ->
Claims_json = claims_to_plaintext(Claims),
Unencrypted = begin
_pipe = gose@jwe:new_pbes2(Alg, Enc),
gose@jwe:with_typ(_pipe, <<"JWT"/utf8>>)
end,
Unencrypted@1 = case Kid of
{some, K} ->
gose@jwe:with_kid(Unencrypted, K);
none ->
Unencrypted
end,
gleam@result:'try'(
gose@jwe:encrypt_with_password(Unencrypted@1, Password, Claims_json),
fun(Encrypted) -> _pipe@1 = gose@jwe:serialize_compact(Encrypted),
gleam@result:map(
_pipe@1,
fun(Token) ->
{encrypted_jwt,
gose@jwe:alg(Encrypted),
Enc,
Kid,
Claims,
Claims_json,
Token}
end
) end
).
-file("src/gose/encrypted_jwt.gleam", 223).
?DOC(
" Encrypt claims using PBES2 password-based encryption.\n"
"\n"
" Sets `typ: \"JWT\"` in the header.\n"
"\n"
" ## Parameters\n"
"\n"
" - `claims` - The JWT claims to encrypt.\n"
" - `alg` - The PBES2 algorithm.\n"
" - `enc` - The content encryption algorithm.\n"
" - `password` - The password for key derivation.\n"
" - `kid` - Optional key ID to include in the header.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(EncryptedJwt)` with the encrypted JWT ready for\n"
" serialization, or `Error(JoseError(_))` if encryption fails.\n"
).
-spec encrypt_with_password(
gose@jwt:claims(),
gose@jwa:pbes2_alg(),
gose@jwa:enc(),
binary(),
gleam@option:option(binary())
) -> {ok, encrypted_jwt()} | {error, gose@jwt:jwt_error()}.
encrypt_with_password(Claims, Alg, Enc, Password, Kid) ->
_pipe = do_encrypt_with_password(Claims, Alg, Enc, Password, Kid),
gleam@result:map_error(_pipe, fun(Field@0) -> {jose_error, Field@0} end).
-file("src/gose/encrypted_jwt.gleam", 283).
?DOC(
" Serialize a decrypted encrypted JWT to compact format.\n"
"\n"
" ## Parameters\n"
"\n"
" - `jwt` - A `EncryptedJwt` from `encrypt_with_key`,\n"
" `encrypt_with_password`, or `decrypt_and_validate`.\n"
"\n"
" ## Returns\n"
"\n"
" The JWE compact serialization string.\n"
).
-spec serialize(encrypted_jwt()) -> binary().
serialize(Jwt) ->
erlang:element(7, Jwt).
-file("src/gose/encrypted_jwt.gleam", 314).
-spec parse_jwe(binary()) -> {ok,
gose@jwe:jwe(gose@jwe:encrypted(), nil, gose@jwe:parsed())} |
{error, gose@jwt:jwt_error()}.
parse_jwe(Token) ->
_pipe = gose@jwe:parse_compact(Token),
gleam@result:map_error(
_pipe,
fun gose@jwt:gose_error_to_malformed_token_error/1
).
-file("src/gose/encrypted_jwt.gleam", 303).
?DOC(
" Peek at the header fields from a token without decrypting.\n"
"\n"
" ## Parameters\n"
"\n"
" - `token` - The JWE compact serialization string.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(PeekHeaders)` with the algorithm, encryption, and optional key ID\n"
" from the header, or `Error(MalformedToken(_))` if the token cannot be\n"
" parsed.\n"
).
-spec peek_headers(binary()) -> {ok, peek_headers()} |
{error, gose@jwt:jwt_error()}.
peek_headers(Token) ->
_pipe = parse_jwe(Token),
gleam@result:map(
_pipe,
fun(Parsed_jwe) ->
{peek_headers,
gose@jwe:alg(Parsed_jwe),
gose@jwe:enc(Parsed_jwe),
gleam@option:from_result(gose@jwe:kid(Parsed_jwe))}
end
).
-file("src/gose/encrypted_jwt.gleam", 424).
?DOC(
" Decode an encrypted JWT's claims using a custom decoder.\n"
"\n"
" This allows extracting claims directly into your own types using\n"
" `gleam/dynamic/decode`. The decoder receives the raw claims JSON.\n"
"\n"
" ## Parameters\n"
"\n"
" - `jwt` - A verified (decrypted) encrypted JWT.\n"
" - `decoder` - A `gleam/dynamic/decode` decoder for the claims.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(a)` with the decoded claims value, or\n"
" `Error(ClaimDecodingFailed(_))` if decoding fails.\n"
).
-spec decode(encrypted_jwt(), gleam@dynamic@decode:decoder(TCP)) -> {ok, TCP} |
{error, gose@jwt:jwt_error()}.
decode(Jwt, Decoder) ->
_pipe = gleam@json:parse_bits(erlang:element(6, Jwt), Decoder),
gleam@result:replace_error(
_pipe,
{claim_decoding_failed, <<"failed to decode claims"/utf8>>}
).
-file("src/gose/encrypted_jwt.gleam", 441).
?DOC(
" Get the key encryption algorithm (`alg`) from a verified encrypted JWT.\n"
"\n"
" ## Parameters\n"
"\n"
" - `jwt` - The verified encrypted JWT.\n"
"\n"
" ## Returns\n"
"\n"
" The `JweAlg` from the token's header.\n"
).
-spec alg(encrypted_jwt()) -> gose@jwa:jwe_alg().
alg(Jwt) ->
erlang:element(2, Jwt).
-file("src/gose/encrypted_jwt.gleam", 454).
?DOC(
" Get the content encryption algorithm (`enc`) from a verified encrypted JWT.\n"
"\n"
" ## Parameters\n"
"\n"
" - `jwt` - The verified encrypted JWT.\n"
"\n"
" ## Returns\n"
"\n"
" The `Enc` from the token's header.\n"
).
-spec enc(encrypted_jwt()) -> gose@jwa:enc().
enc(Jwt) ->
erlang:element(3, Jwt).
-file("src/gose/encrypted_jwt.gleam", 471).
?DOC(
" Get the key ID (kid) from a verified encrypted JWT header.\n"
"\n"
" **Security Warning:** The `kid` value comes from the token and is untrusted\n"
" input. If you use it to look up keys (from a database, filesystem, or key\n"
" store), you must sanitize it first to prevent injection attacks.\n"
"\n"
" ## Parameters\n"
"\n"
" - `jwt` - The verified encrypted JWT.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(String)` with the key ID, or `Error(Nil)` if no kid was set.\n"
).
-spec kid(encrypted_jwt()) -> {ok, binary()} | {error, nil}.
kid(Jwt) ->
gleam@option:to_result(erlang:element(4, Jwt), nil).
-file("src/gose/encrypted_jwt.gleam", 475).
-spec decryptor_options(decryptor()) -> gose@jwt:jwt_validation_options().
decryptor_options(Decryptor) ->
erlang:element(5, Decryptor).
-file("src/gose/encrypted_jwt.gleam", 517).
-spec build_jwe_decryptor(decryptor(), list(gose@jwk:jwk())) -> {ok,
gose@jwe:decryptor()} |
{error, gose@jwt:jwt_error()}.
build_jwe_decryptor(Decryptor, Decryption_keys) ->
case Decryptor of
{key_decryptor, Alg, Enc, _, _} ->
_pipe = gose@jwe:key_decryptor(Alg, Enc, Decryption_keys),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {jose_error, Field@0} end
);
{password_decryptor, Alg@1, Enc@1, Password, _} ->
{ok, gose@jwe:password_decryptor(Alg@1, Enc@1, Password)}
end.
-file("src/gose/encrypted_jwt.gleam", 530).
-spec gose_error_to_decryption_failed(gose:gose_error()) -> gose@jwt:jwt_error().
gose_error_to_decryption_failed(Err) ->
{decryption_failed, gose:error_message(Err)}.
-file("src/gose/encrypted_jwt.gleam", 534).
-spec parse_plaintext_claims(bitstring()) -> {ok, gose@jwt:claims()} |
{error, gose@jwt:jwt_error()}.
parse_plaintext_claims(Plaintext) ->
gose@jwt:parse_claims_bits(Plaintext).
-file("src/gose/encrypted_jwt.gleam", 540).
-spec require_matching_algorithms(
decryptor(),
gose@jwa:jwe_alg(),
gose@jwa:enc()
) -> {ok, nil} | {error, gose@jwt:jwt_error()}.
require_matching_algorithms(Decryptor, Actual_alg, Actual_enc) ->
{Expected_alg, Expected_enc} = case Decryptor of
{key_decryptor, Alg, Enc, _, _} ->
{Alg, Enc};
{password_decryptor, Alg@1, Enc@1, _, _} ->
{{jwe_pbes2, Alg@1}, Enc@1}
end,
case (Expected_alg /= Actual_alg) orelse (Expected_enc /= Actual_enc) of
true ->
{error,
{jwe_algorithm_mismatch,
Expected_alg,
Expected_enc,
Actual_alg,
Actual_enc}};
false ->
{ok, nil}
end.
-file("src/gose/encrypted_jwt.gleam", 562).
-spec select_decryption_keys(
decryptor(),
gleam@option:option(binary()),
gose@jwt:kid_policy()
) -> {ok, list(gose@jwk:jwk())} | {error, gose@jwt:jwt_error()}.
select_decryption_keys(Decryptor, Token_kid, Kid_policy) ->
case Decryptor of
{password_decryptor, _, _, _, _} ->
{ok, []};
{key_decryptor, _, _, Keys, _} ->
gose@jwt:select_keys_by_policy(Keys, Token_kid, Kid_policy)
end.
-file("src/gose/encrypted_jwt.gleam", 479).
-spec decrypt_token(decryptor(), binary()) -> {ok,
{bitstring(),
gose@jwa:jwe_alg(),
gose@jwa:enc(),
gleam@option:option(binary())}} |
{error, gose@jwt:jwt_error()}.
decrypt_token(Decryptor, Token) ->
gleam@result:'try'(
begin
_pipe = gose@jwe:parse_compact(Token),
gleam@result:map_error(
_pipe,
fun gose@jwt:gose_error_to_malformed_token_error/1
)
end,
fun(Parsed_jwe) ->
Actual_alg = gose@jwe:alg(Parsed_jwe),
Actual_enc = gose@jwe:enc(Parsed_jwe),
Token_kid = gleam@option:from_result(gose@jwe:kid(Parsed_jwe)),
gleam@result:'try'(
require_matching_algorithms(Decryptor, Actual_alg, Actual_enc),
fun(_) ->
Options = decryptor_options(Decryptor),
gleam@result:'try'(
select_decryption_keys(
Decryptor,
Token_kid,
erlang:element(8, Options)
),
fun(Decryption_keys) ->
gleam@result:'try'(
build_jwe_decryptor(Decryptor, Decryption_keys),
fun(Jwe_decryptor) ->
gleam@result:'try'(
begin
_pipe@1 = gose@jwe:decrypt(
Jwe_decryptor,
Parsed_jwe
),
gleam@result:map_error(
_pipe@1,
fun gose_error_to_decryption_failed/1
)
end,
fun(Plaintext) ->
{ok,
{Plaintext,
Actual_alg,
Actual_enc,
Token_kid}}
end
)
end
)
end
)
end
)
end
).
-file("src/gose/encrypted_jwt.gleam", 341).
?DOC(
" Decrypt an encrypted JWT, skipping all claim validation.\n"
"\n"
" **Warning:** This skips expiration, not-before, issuer, and audience checks.\n"
" Use only when you have a legitimate reason to bypass validation, such as\n"
" inspecting claims before deciding on validation policy.\n"
"\n"
" Still enforces algorithm pinning for security. **Note:** `kid_policy` only\n"
" applies to key-based decryptors, not password-based decryptors.\n"
"\n"
" ## Parameters\n"
"\n"
" - `decryptor` - A `Decryptor` created by `key_decryptor` or\n"
" `password_decryptor`.\n"
" - `token` - The JWE compact serialization string.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(EncryptedJwt)` with the decrypted JWT and accessible\n"
" claims, `Error(JweAlgorithmMismatch(_))` if the token's algorithms\n"
" don't match, or `Error(DecryptionFailed(_))` if decryption fails.\n"
).
-spec dangerously_decrypt_and_skip_validation(decryptor(), binary()) -> {ok,
encrypted_jwt()} |
{error, gose@jwt:jwt_error()}.
dangerously_decrypt_and_skip_validation(Decryptor, Token) ->
gleam@result:'try'(
decrypt_token(Decryptor, Token),
fun(_use0) ->
{Plaintext, Actual_alg, Actual_enc, Kid} = _use0,
_pipe = parse_plaintext_claims(Plaintext),
gleam@result:map(
_pipe,
fun(Claims) ->
{encrypted_jwt,
Actual_alg,
Actual_enc,
Kid,
Claims,
Plaintext,
Token}
end
)
end
).
-file("src/gose/encrypted_jwt.gleam", 388).
?DOC(
" Decrypt an encrypted JWT and validate its claims using a Decryptor.\n"
"\n"
" Checks:\n"
" 1. Token's `alg` and `enc` headers match the decryptor's expected algorithms\n"
" 2. Decryption succeeds with one of the decryptor's keys\n"
" 3. Claims pass validation (exp, nbf, iss, aud per options)\n"
"\n"
" When multiple keys are configured:\n"
" - Keys with matching `kid` are tried first (if token has `kid` header)\n"
" - `kid_policy` controls kid header enforcement (see `KidPolicy` type)\n"
" - With `NoKidRequirement`, all keys are tried with matching keys prioritized\n"
"\n"
" ## Parameters\n"
"\n"
" - `decryptor` - A `Decryptor` created by `key_decryptor` or\n"
" `password_decryptor`.\n"
" - `token` - The JWE compact serialization string.\n"
" - `now` - The current timestamp for time-based claim validation.\n"
"\n"
" ## Returns\n"
"\n"
" `Ok(EncryptedJwt)` with the decrypted and validated JWT,\n"
" `Error(JweAlgorithmMismatch(_))` if the token's algorithms don't match\n"
" the decryptor's expected algorithms, `Error(DecryptionFailed(_))` if\n"
" decryption fails, or a claim validation error (`TokenExpired`,\n"
" `TokenNotYetValid`, etc.) if claim validation fails.\n"
).
-spec decrypt_and_validate(
decryptor(),
binary(),
gleam@time@timestamp:timestamp()
) -> {ok, encrypted_jwt()} | {error, gose@jwt:jwt_error()}.
decrypt_and_validate(Decryptor, Token, Now) ->
gleam@result:'try'(
decrypt_token(Decryptor, Token),
fun(_use0) ->
{Plaintext, Actual_alg, Actual_enc, Kid} = _use0,
gleam@result:'try'(
parse_plaintext_claims(Plaintext),
fun(Claims) ->
Options = decryptor_options(Decryptor),
_pipe = gose@jwt:validate_claims(Claims, Now, Options),
gleam@result:replace(
_pipe,
{encrypted_jwt,
Actual_alg,
Actual_enc,
Kid,
Claims,
Plaintext,
Token}
)
end
)
end
).