Skip to content

Commit

Permalink
jwt-verify, jwt-generate: Added option to output JSON to command
Browse files Browse the repository at this point in the history
Updated man pages as well. Pulled in a few tweaks as well.

Signed-off-by: Ben Collins <[email protected]>
  • Loading branch information
benmcollins committed Jan 14, 2025
1 parent ba95a07 commit 797d10d
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 86 deletions.
10 changes: 9 additions & 1 deletion tools/jwt-generate.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.\" Automatically generated by Pandoc 3.6.1
.\" Automatically generated by Pandoc 3.6.2
.\"
.TH "JWT\-GENERATE" "1" "" "jwt\-generate User Manual" "LibJWT C Library"
.SH NAME
Expand All @@ -25,6 +25,14 @@ You can specify claims using the \f[B]\-c\f[R] option.
By default, \f[B]jwt\-generate\f[R] will add the \f[B]iat\f[R] claim,
which is \f[B]Issued At\f[R] and is the time in seconds since the
\f[I]Unix Epcoch\f[R].
.PP
When using the \f[B]\-\-verbose\f[R] option, \f[B]jwt\-generate\f[R]
will print the JSON \f[I]HEADER\f[R] and \f[I]PAYLOAD\f[R] to
\f[B]stdout\f[R].
If used in conjuction with \f[B]\-\-print\f[R], the JSON will be piped
to the command\[cq]s \f[B]stdin\f[R].
One use for this is to pass it through \f[B]jq \-C\f[R] for indenting
and colorization.
.SS Options
.TP
\f[B]\-h\f[R], \f[B]\-\-help\f[R]
Expand Down
5 changes: 5 additions & 0 deletions tools/jwt-generate.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ One token will be generated for each call. You can specify claims using the
**-c** option. By default, **jwt-generate** will add the **iat** claim, which
is **Issued At** and is the time in seconds since the *Unix Epcoch*.

When using the **\-\-verbose** option, **jwt-generate** will print the JSON
_HEADER_ and _PAYLOAD_ to **stdout**. If used in conjuction with **\-\-print**,
the JSON will be piped to the command's **stdin**. One use for this is to pass
it through **jq -C** for indenting and colorization.

## Options

**\-h**, **\-\-help**
Expand Down
69 changes: 28 additions & 41 deletions tools/jwt-generate.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <string.h>
#include <libgen.h>

#include "jwt-util.h"

extern const char *__progname;

_Noreturn static void usage(const char *error, int exit_state)
Expand All @@ -32,6 +34,7 @@ Generate and (optionally) sign a JSON Web Token\n\
-a, --algorithm=ALG JWT algorithm to use (e.g. ES256). Only needed if the\n\
key provided with -k does not have an \"alg\"\n\
attribute\n\
-p, --print=CMD When printing JSON, pipe through CMD\n\
-k, --key=FILE Filename containing a JSON Web Key\n\
-c, --claim=t:k=v Add a claim to the JWT\n\
t One of i, s, or b for integer, string or boolean\n\
Expand All @@ -46,40 +49,16 @@ Generate and (optionally) sign a JSON Web Token\n\
they are not added until just before signing.\n\
\n\
This program will encode and sign a token in JWT format.\n\
\n\
For the --print option, output will be piped to the command's stdin. This\n\
is useful if you wanted to use something like `jq -C`.\n\
\n\
If you need to convert a key to JWT (e.g. from PEM or DER format) see\n\
key2jwk(1).\n", __progname);

exit(exit_state);
}

static int __gen_wcb(jwt_t *jwt, jwt_config_t *config)
{
jwt_value_t jval;
int ret;

if (config == NULL)
return 1;

jwt_set_GET_JSON(&jval, NULL);
jval.pretty = 1;
ret = jwt_header_get(jwt, &jval);
if (!ret) {
printf("\033[0;95m[HEADER]\033[0m\n\033[0;96m%s\033[0m\n",
jval.json_val);
free(jval.json_val);
}

jwt_set_GET_JSON(&jval, NULL);
jval.pretty = 1;
ret = jwt_grant_get(jwt, &jval);
if (!ret) {
printf("\033[0;95m[PAYLOAD]\033[0m\n\033[0;96m%s\033[0m\n",
jval.json_val);
free(jval.json_val);
}
return 0;
}

int main(int argc, char *argv[])
{
char *t = NULL, *k = NULL, *v = NULL;
Expand All @@ -95,13 +74,14 @@ int main(int argc, char *argv[])
int verbose = 0;
int quiet = 0;

char *optstr = "hk:a:c:j:lvq";
char *optstr = "hk:a:c:j:lvqp:";
struct option opttbl[] = {
{ "help", no_argument, NULL, 'h' },
{ "key", required_argument, NULL, 'k' },
{ "algorithm", required_argument, NULL, 'a' },
{ "claim", required_argument, NULL, 'c' },
{ "json", required_argument, NULL, 'j' },
{ "print", required_argument, NULL, 'p' },
{ "list", no_argument, NULL, 'l' },
{ "verbose", no_argument, NULL, 'v' },
{ "quiet", no_argument, NULL, 'q' },
Expand Down Expand Up @@ -130,6 +110,10 @@ int main(int argc, char *argv[])
verbose = 1;
break;

case 'p':
pipe_cmd = optarg;
break;

case 'l':
printf("Algorithms supported:\n");
for (alg = JWT_ALG_NONE; alg < JWT_ALG_INVAL; alg++)
Expand Down Expand Up @@ -232,7 +216,7 @@ int main(int argc, char *argv[])

if (jwks_item_alg(item) == JWT_ALG_NONE &&
alg == JWT_ALG_NONE) {
usage("Key does not contain an \"alg\" attribute and no --alg given",
usage("No \"alg\" attribute in key and --alg not given",
EXIT_FAILURE);
}

Expand All @@ -251,28 +235,31 @@ int main(int argc, char *argv[])
}
}

if (verbose && jwt_builder_setcb(builder, __gen_wcb, NULL)) {
if (verbose && jwt_builder_setcb(builder, __jwt_wcb, NULL)) {
fprintf(stderr, "ERR setting callback: %s\n",
jwt_builder_error_msg(builder));
exit(EXIT_FAILURE);
}

json_fp = stderr;

if (item && !quiet)
printf("\xF0\x9F\x94\x91 \033[0;92m[KEY]\033[0m %s\n",
key_file);
fprintf(stderr, "\xF0\x9F\x94\x91 \033[0;92m[KEY]\033[0m %s\n",
key_file);

if (!quiet) {
printf("\xF0\x9F\x93\x83 ");
fprintf(stderr, "\xF0\x9F\x93\x83 ");
if (item && jwks_item_alg(item) != JWT_ALG_NONE) {
printf("\033[0;92m[ALG]\033[0m %s (from key)",
fprintf(stderr, "\033[0;92m[ALG]\033[0m %s (from key)",
jwt_alg_str(jwks_item_alg(item)));
alg = jwks_item_alg(item);
} else if (alg != JWT_ALG_NONE) {
printf("\033[0;92m[ALG]\033[0m %s (from options)", jwt_alg_str(alg));
fprintf(stderr, "\033[0;92m[ALG]\033[0m %s (from options)",
jwt_alg_str(alg));
} else {
printf("\033[0;91m[ALG]\033[0m %s", jwt_alg_str(alg));
fprintf(stderr, "\033[0;91m[ALG]\033[0m %s", jwt_alg_str(alg));
}
printf("\n");
fprintf(stderr, "\n");
}

token = jwt_builder_generate(builder);
Expand All @@ -285,10 +272,10 @@ int main(int argc, char *argv[])
if (quiet) {
printf("%s\n", token);
} else {
printf("%s %s[TOK]\033[0m %s\n", alg == JWT_ALG_NONE ?
fprintf(stderr, "%s %s[TOK]\033[0m \n", alg == JWT_ALG_NONE ?
"\xF0\x9F\x94\x93" : "\xF0\x9F\x94\x90",
alg == JWT_ALG_NONE ? "\033[0;93m" : "\033[0;92m",
token);
alg == JWT_ALG_NONE ? "\033[0;93m" : "\033[0;92m");
printf("%s\n", token);
}

free(token);
Expand Down
86 changes: 86 additions & 0 deletions tools/jwt-util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* Copyright (C) 2024-2025 maClara, LLC <[email protected]>
This file is part of the JWT C Library
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/. */

static char *pipe_cmd;

static FILE *json_fp;

static void write_json(const char *title, const char *str)
{
char *argv[4] = { "/bin/sh", "-c", NULL, NULL };
int pipe_fd[2];
int myfd = 0;
pid_t pid;
int status;

if (json_fp == NULL)
json_fp = stdout;

if (pipe_cmd) {
pipe(pipe_fd);

argv[2] = pipe_cmd;
pid = fork();

if (pid == 0) {
close(pipe_fd[1]);

if (json_fp == stderr)
dup2(STDERR_FILENO, STDOUT_FILENO);
dup2(pipe_fd[0], STDIN_FILENO);
close(pipe_fd[0]);

execvp(argv[0], argv);

perror("execvp");
exit(EXIT_FAILURE);
} else {
close(pipe_fd[0]);
myfd = pipe_fd[1];
}
}

fprintf(json_fp, "\033[0;95m[%s]\033[0m\n", title);

if (myfd) {
write(myfd, str, strlen(str));
} else {
fprintf(json_fp, "\033[0;96m%s\033[0m\n", str);
}

if (myfd) {
close(myfd);
waitpid(pid, &status, 0);
}
}

static int __jwt_wcb(jwt_t *jwt, jwt_config_t *config)
{
jwt_value_t jval;
int ret;

if (config == NULL)
return 1;

jwt_set_GET_JSON(&jval, NULL);
jval.pretty = 1;
ret = jwt_header_get(jwt, &jval);
if (!ret) {
write_json("HEADER", jval.json_val);
free(jval.json_val);
}

jwt_set_GET_JSON(&jval, NULL);
jval.pretty = 1;
ret = jwt_grant_get(jwt, &jval);
if (!ret) {
write_json("PAYLOAD", jval.json_val);
free(jval.json_val);
}

return 0;
}
15 changes: 14 additions & 1 deletion tools/jwt-verify.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.\" Automatically generated by Pandoc 3.6.1
.\" Automatically generated by Pandoc 3.6.2
.\"
.TH "JWT\-VERIFY" "1" "" "jwt\-verify User Manual" "LibJWT C Library"
.SH NAME
Expand Down Expand Up @@ -29,6 +29,14 @@ Tokens may be passed on the command line, after any options, separated
by spaces, or passed via \f[B]stdin\f[R], one per line.
To use \f[B]stdin\f[R], you must pass \f[B]\-\f[R] as the last and only
argument after any options.
.PP
When using the \f[B]\-\-verbose\f[R] option, \f[B]jwt\-verify\f[R] will
print the JSON \f[I]HEADER\f[R] and \f[I]PAYLOAD\f[R] to
\f[B]stdout\f[R].
If used in conjuction with \f[B]\-\-print\f[R], the JSON will be piped
to the command\[cq]s \f[B]stdin\f[R].
One use for this is to pass it through \f[B]jq \-C\f[R] for indenting
and colorization.
.SS Options
.TP
\f[B]\-h\f[R], \f[B]\-\-help\f[R]
Expand Down Expand Up @@ -56,6 +64,11 @@ Path to a file containing a key in JSON Web Key format.
If your keys are in PEM or DER (or some other common format that
\f[I]OpenSSL\f[R] understands), then you can convert it to a JWK with
the \f[B]key2jwk(1)\f[R] tool.
.TP
\f[B]\-p\f[R] \f[I]CMD\f[R], \f[B]\-\-print\f[R]=\f[I]CMD\f[R]
Pipe JSON of header and payload to \f[I]CMD\f[R] through its
\f[B]stdin\f[R].
This option only makes sense with \f[B]\-\-verbose\f[R].
.SH BUGS
See GitHub Issues: \c
.UR https://github.com/benmcollins/libjwt/issues
Expand Down
9 changes: 9 additions & 0 deletions tools/jwt-verify.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Tokens may be passed on the command line, after any options, separated
by spaces, or passed via **stdin**, one per line. To use **stdin**, you
must pass **-** as the last and only argument after any options.

When using the **\-\-verbose** option, **jwt-verify** will print the JSON
_HEADER_ and _PAYLOAD_ to **stdout**. If used in conjuction with **\-\-print**,
the JSON will be piped to the command's **stdin**. One use for this is to pass
it through **jq -C** for indenting and colorization.

## Options

**\-h**, **\-\-help**
Expand All @@ -54,6 +59,10 @@ must pass **-** as the last and only argument after any options.
in PEM or DER (or some other common format that _OpenSSL_ understands), then
you can convert it to a JWK with the **key2jwk(1)** tool.

**\-p** _CMD_, **\-\-print**=_CMD_
~ Pipe JSON of header and payload to _CMD_ through its **stdin**. This option
only makes sense with **\-\-verbose**.

# BUGS

See GitHub Issues: <https://github.com/benmcollins/libjwt/issues>
Expand Down
Loading

0 comments on commit 797d10d

Please sign in to comment.