added JWT demo programs
This commit is contained in:
parent
4d589ee8bf
commit
162bb122fc
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
*.o
|
||||||
|
deps
|
||||||
|
jansson
|
||||||
|
libjwt
|
||||||
|
src/server
|
||||||
|
src/jwt_demo_hs256
|
||||||
|
src/jwt_demo_rs256
|
||||||
|
.gitignore
|
@ -11,7 +11,10 @@ LDLIBS=-L$(DEP_LIB_DIR) -ljwt -ljansson -lcrypto -ldl
|
|||||||
HEADERS=socket.h http.h hexdump.h buffer.h bufio.h
|
HEADERS=socket.h http.h hexdump.h buffer.h bufio.h
|
||||||
OBJ=main.o socket.o hexdump.o http.o bufio.o
|
OBJ=main.o socket.o hexdump.o http.o bufio.o
|
||||||
|
|
||||||
all: server
|
|
||||||
|
OTHERS=jwt_demo_rs256 jwt_demo_hs256
|
||||||
|
|
||||||
|
all: server $(OTHERS)
|
||||||
|
|
||||||
$(OBJ) : $(HEADERS)
|
$(OBJ) : $(HEADERS)
|
||||||
|
|
||||||
@ -19,4 +22,4 @@ server: $(OBJ)
|
|||||||
$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LDLIBS)
|
$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LDLIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
/bin/rm $(OBJ)
|
/bin/rm $(OBJ) $(OTHERS)
|
||||||
|
36
src/http.c
36
src/http.c
@ -34,19 +34,19 @@ char * server_root; // root from which static files are served
|
|||||||
|
|
||||||
|
|
||||||
/* Parse HTTP request line, setting req_method, req_path, and req_version. */
|
/* Parse HTTP request line, setting req_method, req_path, and req_version. */
|
||||||
static int
|
static bool
|
||||||
http_parse_request(struct http_transaction *ta)
|
http_parse_request(struct http_transaction *ta)
|
||||||
{
|
{
|
||||||
size_t req_offset;
|
size_t req_offset;
|
||||||
ssize_t len = bufio_readline(ta->client->bufio, &req_offset);
|
ssize_t len = bufio_readline(ta->client->bufio, &req_offset);
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return len;
|
return false;
|
||||||
|
|
||||||
char *request = bufio_offset2ptr(ta->client->bufio, req_offset);
|
char *request = bufio_offset2ptr(ta->client->bufio, req_offset);
|
||||||
char *endptr;
|
char *endptr;
|
||||||
char *method = strtok_r(request, " ", &endptr);
|
char *method = strtok_r(request, " ", &endptr);
|
||||||
if (method == NULL)
|
if (method == NULL)
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
if (!strcmp(method, "GET"))
|
if (!strcmp(method, "GET"))
|
||||||
ta->req_method = HTTP_GET;
|
ta->req_method = HTTP_GET;
|
||||||
@ -57,37 +57,37 @@ http_parse_request(struct http_transaction *ta)
|
|||||||
|
|
||||||
char *req_path = strtok_r(NULL, " ", &endptr);
|
char *req_path = strtok_r(NULL, " ", &endptr);
|
||||||
if (req_path == NULL)
|
if (req_path == NULL)
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
ta->req_path = bufio_ptr2offset(ta->client->bufio, req_path);
|
ta->req_path = bufio_ptr2offset(ta->client->bufio, req_path);
|
||||||
|
|
||||||
char *http_version = strtok_r(NULL, CRLF, &endptr);
|
char *http_version = strtok_r(NULL, CRLF, &endptr);
|
||||||
if (http_version == NULL) // would be HTTP 0.9
|
if (http_version == NULL) // would be HTTP 0.9
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
if (!strcmp(http_version, "HTTP/1.1"))
|
if (!strcmp(http_version, "HTTP/1.1"))
|
||||||
ta->req_version = HTTP_1_1;
|
ta->req_version = HTTP_1_1;
|
||||||
else if (!strcmp(http_version, "HTTP/1.0"))
|
else if (!strcmp(http_version, "HTTP/1.0"))
|
||||||
ta->req_version = HTTP_1_0;
|
ta->req_version = HTTP_1_0;
|
||||||
else
|
else
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process HTTP headers. */
|
/* Process HTTP headers. */
|
||||||
static int
|
static bool
|
||||||
http_process_headers(struct http_transaction *ta)
|
http_process_headers(struct http_transaction *ta)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
size_t header_offset;
|
size_t header_offset;
|
||||||
ssize_t len = bufio_readline(ta->client->bufio, &header_offset);
|
ssize_t len = bufio_readline(ta->client->bufio, &header_offset);
|
||||||
char *header = bufio_offset2ptr(ta->client->bufio, header_offset);
|
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
return len;
|
return false;
|
||||||
|
|
||||||
|
char *header = bufio_offset2ptr(ta->client->bufio, header_offset);
|
||||||
if (len == 2 && STARTS_WITH(header, CRLF)) // empty CRLF
|
if (len == 2 && STARTS_WITH(header, CRLF)) // empty CRLF
|
||||||
return 0;
|
return true;
|
||||||
|
|
||||||
header[len-2] = '\0';
|
header[len-2] = '\0';
|
||||||
/* Each header field consists of a name followed by a
|
/* Each header field consists of a name followed by a
|
||||||
@ -100,7 +100,7 @@ http_process_headers(struct http_transaction *ta)
|
|||||||
char *field_value = strtok_r(NULL, " \t", &endptr); // skip leading & trailing OWS
|
char *field_value = strtok_r(NULL, " \t", &endptr); // skip leading & trailing OWS
|
||||||
|
|
||||||
if (field_name == NULL)
|
if (field_name == NULL)
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
// printf("Header: %s: %s\n", field_name, field_value);
|
// printf("Header: %s: %s\n", field_name, field_value);
|
||||||
if (!strcasecmp(field_name, "Content-Length")) {
|
if (!strcasecmp(field_name, "Content-Length")) {
|
||||||
@ -109,8 +109,6 @@ http_process_headers(struct http_transaction *ta)
|
|||||||
|
|
||||||
/* Handle other headers here. */
|
/* Handle other headers here. */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MAX_HEADER_LEN = 2048;
|
const int MAX_HEADER_LEN = 2048;
|
||||||
@ -331,16 +329,16 @@ http_handle_transaction(struct http_client *self)
|
|||||||
memset(&ta, 0, sizeof ta);
|
memset(&ta, 0, sizeof ta);
|
||||||
ta.client = self;
|
ta.client = self;
|
||||||
|
|
||||||
if (http_parse_request(&ta) == -1)
|
if (!http_parse_request(&ta))
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
if (http_process_headers(&ta) == -1)
|
if (!http_process_headers(&ta))
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
if (ta.req_content_len > 0) {
|
if (ta.req_content_len > 0) {
|
||||||
int rc = bufio_read(self->bufio, ta.req_content_len, &ta.req_body);
|
int rc = bufio_read(self->bufio, ta.req_content_len, &ta.req_body);
|
||||||
if (rc != ta.req_content_len)
|
if (rc != ta.req_content_len)
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
// To see the body, use this:
|
// To see the body, use this:
|
||||||
// char *body = bufio_offset2ptr(ta.client->bufio, ta.req_body);
|
// char *body = bufio_offset2ptr(ta.client->bufio, ta.req_body);
|
||||||
|
56
src/jwt_demo_hs256.c
Normal file
56
src/jwt_demo_hs256.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Quick demo of how to use libjwt using a HS256.
|
||||||
|
*
|
||||||
|
* @author gback, CS 3214, Spring 2018
|
||||||
|
*/
|
||||||
|
#include <jwt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static const char * NEVER_EMBED_A_SECRET_IN_CODE = "supa secret";
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
jwt_t *mytoken;
|
||||||
|
|
||||||
|
if (jwt_new(&mytoken))
|
||||||
|
perror("jwt_new"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_add_grant(mytoken, "sub", "user0"))
|
||||||
|
perror("jwt_add_grant sub"), exit(-1);
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (jwt_add_grant_int(mytoken, "iat", now))
|
||||||
|
perror("jwt_add_grant iat"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_add_grant_int(mytoken, "exp", now + 3600 * 24))
|
||||||
|
perror("jwt_add_grant exp"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_set_alg(mytoken, JWT_ALG_HS256,
|
||||||
|
(unsigned char *)NEVER_EMBED_A_SECRET_IN_CODE, strlen(NEVER_EMBED_A_SECRET_IN_CODE)))
|
||||||
|
perror("jwt_set_alg"), exit(-1);
|
||||||
|
|
||||||
|
printf("dump:\n");
|
||||||
|
if (jwt_dump_fp(mytoken, stdout, 1))
|
||||||
|
perror("jwt_dump_fp"), exit(-1);
|
||||||
|
|
||||||
|
char *encoded = jwt_encode_str(mytoken);
|
||||||
|
if (encoded == NULL)
|
||||||
|
perror("jwt_encode_str"), exit(-1);
|
||||||
|
|
||||||
|
printf("encoded as %s\nTry entering this at jwt.io\n", encoded);
|
||||||
|
|
||||||
|
jwt_t *ymtoken;
|
||||||
|
if (jwt_decode(&ymtoken, encoded,
|
||||||
|
(unsigned char *)NEVER_EMBED_A_SECRET_IN_CODE, strlen(NEVER_EMBED_A_SECRET_IN_CODE)))
|
||||||
|
perror("jwt_decode"), exit(-1);
|
||||||
|
|
||||||
|
char *grants = jwt_get_grants_json(ymtoken, NULL); // NULL means all
|
||||||
|
if (grants == NULL)
|
||||||
|
perror("jwt_get_grants_json"), exit(-1);
|
||||||
|
|
||||||
|
printf("redecoded: %s\n", grants);
|
||||||
|
}
|
74
src/jwt_demo_rs256.c
Normal file
74
src/jwt_demo_rs256.c
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Quick demo of how to use libjwt using a RS256.
|
||||||
|
* You must have created a private/public key pair like so:
|
||||||
|
*
|
||||||
|
* openssl genpkey -algorithm RSA -out myprivatekey.pem -pkeyopt rsa_keygen_bits:2048
|
||||||
|
* openssl rsa -in myprivatekey.pem -pubout > mykey.pub
|
||||||
|
*
|
||||||
|
* @author gback, CS 3214, Spring 2018
|
||||||
|
*/
|
||||||
|
#include <jwt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
unsigned char private_key[16384];
|
||||||
|
unsigned char public_key[16384];
|
||||||
|
|
||||||
|
static void
|
||||||
|
read_key(unsigned char *key, const char *name)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(name, "r");
|
||||||
|
if (f == NULL)
|
||||||
|
perror("fopen"), exit(-1);
|
||||||
|
size_t len = fread(key, 1, 8192, f);
|
||||||
|
key[len] = '\0';
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
read_key(private_key, "myprivatekey.pem");
|
||||||
|
read_key(public_key, "mykey.pub");
|
||||||
|
|
||||||
|
jwt_t *mytoken;
|
||||||
|
|
||||||
|
if (jwt_new(&mytoken))
|
||||||
|
perror("jwt_new"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_add_grant(mytoken, "sub", "user0"))
|
||||||
|
perror("jwt_add_grant sub"), exit(-1);
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (jwt_add_grant_int(mytoken, "iat", now))
|
||||||
|
perror("jwt_add_grant iat"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_add_grant_int(mytoken, "exp", now + 3600 * 24))
|
||||||
|
perror("jwt_add_grant exp"), exit(-1);
|
||||||
|
|
||||||
|
if (jwt_set_alg(mytoken, JWT_ALG_RS256,
|
||||||
|
private_key, strlen((char *)private_key)))
|
||||||
|
perror("jwt_set_alg"), exit(-1);
|
||||||
|
|
||||||
|
printf("dump:\n");
|
||||||
|
if (jwt_dump_fp(mytoken, stdout, 1))
|
||||||
|
perror("jwt_dump_fp"), exit(-1);
|
||||||
|
|
||||||
|
char *encoded = jwt_encode_str(mytoken);
|
||||||
|
if (encoded == NULL)
|
||||||
|
perror("jwt_encode_str"), exit(-1);
|
||||||
|
|
||||||
|
printf("encoded as %s\nTry entering this at jwt.io\n", encoded);
|
||||||
|
|
||||||
|
jwt_t *ymtoken;
|
||||||
|
if (jwt_decode(&ymtoken, encoded, public_key, strlen((char *)public_key)))
|
||||||
|
perror("jwt_decode"), exit(-1);
|
||||||
|
|
||||||
|
char *grants = jwt_get_grants_json(ymtoken, NULL); // NULL means all
|
||||||
|
if (grants == NULL)
|
||||||
|
perror("jwt_get_grants_json"), exit(-1);
|
||||||
|
|
||||||
|
printf("redecoded: %s\n", grants);
|
||||||
|
}
|
41
src/login.html
Normal file
41
src/login.html
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<!-- minimal demo of expected login functionality -->
|
||||||
|
<body>
|
||||||
|
<form onsubmit="return login(this)">
|
||||||
|
Username: <input name="username" >
|
||||||
|
Password: <input name="password" >
|
||||||
|
<input type="submit"/>
|
||||||
|
</form>
|
||||||
|
<div id="result">The result of the request will appear here.</div>
|
||||||
|
<script>
|
||||||
|
const result = document.getElementById('result');
|
||||||
|
|
||||||
|
function login(form) {
|
||||||
|
fetch("/api/login",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: form.username.value,
|
||||||
|
password: form.password.value
|
||||||
|
})
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.status == 200) {
|
||||||
|
res.json().then((data) => {
|
||||||
|
result.innerText = `Success: token ${JSON.stringify(data)}`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.innerText = `Received: ${res.status} ${res.statusText}`;
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
result.innerText = `Error: ${error.message}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return false; // prevent default action
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
@ -23,6 +23,7 @@ server_loop(char *port_string)
|
|||||||
{
|
{
|
||||||
int accepting_socket = socket_open_bind_listen(port_string, 1024);
|
int accepting_socket = socket_open_bind_listen(port_string, 1024);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
fprintf(stderr, "Waiting for client...\n");
|
||||||
int client_socket = socket_accept_client(accepting_socket);
|
int client_socket = socket_accept_client(accepting_socket);
|
||||||
if (client_socket == -1)
|
if (client_socket == -1)
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user