summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accesstoken.h (renamed from token.h)7
-rw-r--r--configure.ac2
-rw-r--r--request.h6
-rw-r--r--session.h2
-rw-r--r--sessiontoken.h22
-rw-r--r--shopify.c311
6 files changed, 212 insertions, 138 deletions
diff --git a/token.h b/accesstoken.h
index 40f600e..009cdb0 100644
--- a/token.h
+++ b/accesstoken.h
@@ -1,6 +1,7 @@
#include <json.h>
-static inline void token_parse(const char *tok, struct shopify_session *session)
+static inline void accesstoken_parse(const char *tok,
+ struct shopify_session *session)
{
json_tokener *tokener = json_tokener_new();
json_object *obj = json_tokener_parse_ex(tokener, tok, strlen(tok));
@@ -11,8 +12,8 @@ static inline void token_parse(const char *tok, struct shopify_session *session)
"access_token")) {
const char *val = json_object_get_string(
json_object_iter_peek_value(&iter));
- session->token = malloc(strlen(val) + 1);
- strcpy(session->token, val);
+ session->access_token = malloc(strlen(val) + 1);
+ strcpy(session->access_token, val);
} else if (!strcmp(json_object_iter_peek_name(&iter),
"scope")) {
const char *val = json_object_get_string(
diff --git a/configure.ac b/configure.ac
index 08ad556..9a09c88 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AM_PROG_AR
LT_INIT
-PKG_CHECK_MODULES([DEPS], [libmicrohttpd libgcrypt gnutls libpcre2-8 libcurl json-c])
+PKG_CHECK_MODULES([DEPS], [libmicrohttpd libgcrypt gnutls libpcre2-8 libcurl json-c libjwt])
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
diff --git a/request.h b/request.h
index 75bfa65..c782c38 100644
--- a/request.h
+++ b/request.h
@@ -26,7 +26,7 @@ static size_t append(char *data, size_t size, size_t nmemb, char **json)
return realsize;
}
-static inline void request_token(const char *host, const char *key,
+static inline void request_gettoken(const char *host, const char *key,
const char *secret_key, const char *code, char **json)
{
CURL *curl = curl_easy_init();
@@ -55,8 +55,8 @@ static inline void request_graphql(const char *query,
curl_easy_setopt(curl, CURLOPT_URL, url);
struct curl_slist *list = NULL;
curl_slist_append(list, "Content-Type: application/json");
- char header[TOKEN_HEADER_LEN + strlen(session->token) + 1];
- sprintf(header, TOKEN_HEADER, session->token);
+ char header[TOKEN_HEADER_LEN + strlen(session->access_token) + 1];
+ sprintf(header, TOKEN_HEADER, session->access_token);
curl_slist_append(list, header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query);
diff --git a/session.h b/session.h
index e14f976..d306bcc 100644
--- a/session.h
+++ b/session.h
@@ -1,6 +1,6 @@
struct shopify_session {
char *shop;
char *nonce;
- char *token;
+ char *access_token;
char *scope;
};
diff --git a/sessiontoken.h b/sessiontoken.h
new file mode 100644
index 0000000..ec7a462
--- /dev/null
+++ b/sessiontoken.h
@@ -0,0 +1,22 @@
+#include <jwt.h>
+
+static inline bool sessiontoken_isvalid(const char *token, const char *secret)
+{
+ const size_t key_len = strlen(secret) / 2;
+ unsigned char key[key_len];
+ for (size_t i = 0; i < key_len; i++) {
+ char hex[3] = { [2] = '\0' };
+ strncpy(hex, &secret[i], 2);
+ key[i] = strtol(hex, NULL, 16);
+ }
+ jwt_t *jwt = NULL;
+ jwt_decode(&jwt, token, key, key_len);
+ printf("exp: %s\n", jwt_get_grant(jwt, "exp"));
+ printf("nbf: %s\n", jwt_get_grant(jwt, "nbf"));
+ printf("iss: %s\n", jwt_get_grant(jwt, "iss"));
+ printf("dest: %s\n", jwt_get_grant(jwt, "dest"));
+ printf("aud: %s\n", jwt_get_grant(jwt, "aud"));
+ printf("sub: %s\n", jwt_get_grant(jwt, "sub"));
+ jwt_free(jwt);
+ return false;
+}
diff --git a/shopify.c b/shopify.c
index 71904b6..5c716f1 100644
--- a/shopify.c
+++ b/shopify.c
@@ -9,7 +9,8 @@
#include "config.h"
#include "session.h"
#include "request.h"
-#include "token.h"
+#include "accesstoken.h"
+#include "sessiontoken.h"
#define AUTH_URL \
"https://%s/oauth/authorize?client_id=%s&scope=%s&redirect_uri=%s%s"\
@@ -50,12 +51,13 @@ extern inline bool regex_match(const char *);
extern inline void base64_decode(const char *, char **);
extern inline void config_getscopes(const char *, char **);
extern inline void request_init();
-extern inline void request_token(const char *, const char *, const char *,
+extern inline void request_gettoken(const char *, const char *, const char *,
const char *, char **);
extern inline void request_graphql(const char *, const struct shopify_session *,
char **);
extern inline void request_cleanup();
-extern inline void token_parse(const char *, struct shopify_session *);
+extern inline void accesstoken_parse(const char *, struct shopify_session *);
+extern inline bool sessiontoken_isvalid(const char *, const char *);
struct parameter {
char *key;
@@ -93,7 +95,20 @@ static enum MHD_Result iterate(void *cls, enum MHD_ValueKind kind,
(*params)[nparams + 1].key = NULL;
break;
case MHD_HEADER_KIND:
- printf("%s: %s\n", key, val);
+ ;
+ char ***array = cls;
+ if (!strcmp(key, "Authorization")) {
+ static const char *schema = "Bearer ";
+ const size_t schema_len = strlen(schema);
+ const int token_len = strlen(val) - schema_len;
+ if (token_len < 0)
+ break;
+ *array[0] = malloc(token_len + 1);
+ strcpy(*array[0], &val[schema_len]);
+ } else if (!strcmp(key, "Referer")) {
+ *array[1] = malloc(strlen(val) + 1);
+ strcpy(*array[1], val);
+ }
break;
default:
break;
@@ -138,157 +153,193 @@ static enum MHD_Result handle_request(void *cls, struct MHD_Connection *con,
return MHD_YES;
}
MHD_get_connection_values(con, MHD_GET_ARGUMENT_KIND, iterate, &params);
- MHD_get_connection_values(con, MHD_HEADER_KIND, iterate, NULL);
- int nparams = 0;
- while (params[nparams].key)
- nparams++;
- if (!nparams) {
- free(params);
- return MHD_NO;
- }
- qsort(params, nparams, sizeof(struct parameter), keycmp);
- struct parameter *param = NULL;
- char *shop = NULL;
- if ((param = bsearch(&(struct parameter) { "shop" }, params, nparams,
- sizeof(struct parameter), keycmp)))
- shop = param->val;
- if (!shop || !regex_match(shop)) {
- clear(params);
- free(params);
- return MHD_NO;
- }
- char *query = NULL;
- for (int i = 0; i < nparams; i++) {
- const char *key = params[i].key;
- const char *val = params[i].val;
- if (strcmp(key, "hmac")) {
- size_t query_len = query ? strlen(query) : 0;
- bool ampersand_len = i != nparams - 1;
- query = realloc(query, query_len + strlen(key)
- + strlen("=") + strlen(val)
- + ampersand_len + 1);
- query[query_len] = '\0';
- sprintf(query, "%s%s=%s%s", query, key, val,
- ampersand_len ? "&" : "");
- }
- }
- char *hmac = NULL;
- if ((param = bsearch(&(struct parameter) { "hmac" }, params, nparams,
- sizeof(struct parameter), keycmp)))
- hmac = param->val;
struct container *container = cls;
const char *secret_key = container->secret;
- if (!hmac || !crypt_maccmp(secret_key, query, hmac)) {
- free(query);
- clear(params);
- free(params);
- return MHD_NO;
- }
- free(query);
+ const char *app_url = container->app_url;
+ const size_t app_url_len = strlen(app_url);
+ const char *redir_url = container->redir_url;
struct shopify_session *sessions = container->sessions;
int nsessions = 0;
while (sessions[nsessions].shop)
nsessions++;
qsort(sessions, nsessions, sizeof(struct shopify_session), keycmp);
- const char *redir_url = container->redir_url;
- if (!strcmp(url, redir_url)
- && strcmp(((struct parameter *)bsearch(
- &(struct parameter){ "state" },
- params, nparams,
+ char *shop = NULL;
+ size_t shop_len = 0;
+ char *session_token = NULL;
+ struct parameter *param = NULL;
+ int nparams = 0;
+ while (params[nparams].key)
+ nparams++;
+ if (nparams) {
+ qsort(params, nparams, sizeof(struct parameter), keycmp);
+ if ((param = bsearch(&(struct parameter) { "shop" }, params,
+ nparams,
sizeof(struct parameter),
- keycmp))->val,
- ((struct shopify_session *)bsearch(
- &(struct shopify_session){ shop },
- sessions, nsessions,
- sizeof(struct shopify_session),
- keycmp))->nonce)) {
- clear(params);
+ keycmp)))
+ shop = param->val;
+ if (!shop || !regex_match(shop)) {
+ clear(params);
+ free(params);
+ return MHD_NO;
+ }
+ shop_len = strlen(shop);
+ char *query = NULL;
+ for (int i = 0; i < nparams; i++) {
+ const char *key = params[i].key;
+ const char *val = params[i].val;
+ if (strcmp(key, "hmac")) {
+ size_t query_len = query ? strlen(query) : 0;
+ bool ampersand_len = i != nparams - 1;
+ query = realloc(query, query_len + strlen(key)
+ + strlen("=") + strlen(val)
+ + ampersand_len + 1);
+ query[query_len] = '\0';
+ sprintf(query, "%s%s=%s%s", query, key, val,
+ ampersand_len ? "&" : "");
+ }
+ }
+ char *hmac = NULL;
+ if ((param = bsearch(&(struct parameter) { "hmac" }, params,
+ nparams,
+ sizeof(struct parameter),
+ keycmp)))
+ hmac = param->val;
+ if (!hmac || !crypt_maccmp(secret_key, query, hmac)) {
+ free(query);
+ clear(params);
+ free(params);
+ return MHD_NO;
+ }
+ free(query);
+ if (!strcmp(url, redir_url)
+ && strcmp(((struct parameter *)bsearch(
+ &(struct parameter)
+ { "state" }, params,
+ nparams,
+ sizeof(struct parameter),
+ keycmp))->val,
+ ((struct shopify_session *)bsearch(
+ &(struct shopify_session)
+ { shop }, sessions, nsessions,
+ sizeof(struct shopify_session),
+ keycmp))->nonce)) {
+ clear(params);
+ free(params);
+ return MHD_NO;
+ }
+ } else {
free(params);
- return MHD_NO;
+ char *referer = NULL;
+ MHD_get_connection_values(con, MHD_HEADER_KIND, iterate,
+ (char **[]){ &session_token, &referer });
+ if (!session_token || !referer
+ || strncmp(referer, app_url, app_url_len)
+ || referer[app_url_len] != '?'
+ || !&referer[app_url_len + 1]) {
+ if (session_token)
+ free(session_token);
+ if (referer)
+ free(referer);
+ return MHD_NO;
+ }
+ referer = &referer[app_url_len + 1];
+ char *tofree = referer;
+ char *pair = NULL;
+ static const char *key = "shop=";
+ const size_t key_len = strlen(key);
+ while ((pair = strsep(&referer, "&")))
+ if (!strncmp(pair, key, key_len))
+ break;
+ if (!pair || !&pair[key_len]) {
+ free(session_token);
+ free(tofree);
+ return MHD_NO;
+ }
+ pair = &pair[key_len];
+ char *next = strchrnul(pair, '&');
+ shop_len = sizeof(char) * (next - pair);
+ shop = malloc(shop_len + 1);
+ strlcpy(shop, pair, shop_len + 1);
+ printf("Shop: %s\n", shop);
+ free(tofree);
+ if (!regex_match(shop) || !sessiontoken_isvalid(session_token,
+ secret_key)) {
+ free(session_token);
+ free(shop);
+ return MHD_NO;
+ }
+ }
+ char *host = NULL;
+ size_t host_len = 0;
+ bool embedded = false;
+ char *dec_host = NULL;
+ if (params) {
+ host = ((struct parameter *)bsearch(&(struct parameter)
+ { "host" }, params, nparams,
+ sizeof(struct parameter), keycmp))->val;
+ host_len = strlen(host);
+ param = bsearch(&(struct parameter){ "embedded" }, params,
+ nparams, sizeof(struct parameter), keycmp);
+ embedded = param && !strcmp(param->val, "1");
+ base64_decode(host, &dec_host);
}
- const size_t shop_len = strlen(shop);
- char *host = ((struct parameter *)bsearch(&(struct parameter){ "host" },
- params, nparams, sizeof(struct parameter),
- keycmp))->val;
- const size_t host_len = strlen(host);
- param = bsearch(&(struct parameter){ "embedded" }, params, nparams,
- sizeof(struct parameter), keycmp);
- bool embedded = param && !strcmp(param->val, "1");
- char *dec_host;
- base64_decode(host, &dec_host);
- struct shopify_session *session = bsearch(&(struct shopify_session)
- { shop }, sessions, nsessions,
- sizeof(struct shopify_session), keycmp);
const char *key = container->key;
const size_t key_len = strlen(key);
- const char *app_url = container->app_url;
- const size_t app_url_len = strlen(app_url);
const char *app_id = container->app_id;
char header[EMBEDDED_HEADER_LEN + shop_len + 1];
sprintf(header, EMBEDDED_HEADER, shop);
- struct MHD_Response *res;
+ struct shopify_session *session = bsearch(&(struct shopify_session)
+ { shop }, sessions, nsessions,
+ sizeof(struct shopify_session), keycmp);
+ struct MHD_Response *res = NULL;
enum MHD_Result ret = MHD_NO;
if (!strcmp(url, redir_url)) {
const char *code = ((struct parameter *)bsearch(
&(struct parameter){ "code" }, params,
nparams, sizeof(struct parameter),
keycmp))->val;
- char *token = NULL;
- request_token(dec_host, key, secret_key, code, &token);
- token_parse(token, session);
- free(token);
+ char *access_token = NULL;
+ request_gettoken(dec_host, key, secret_key, code,
+ &access_token);
+ accesstoken_parse(access_token, session);
+ free(access_token);
ret = redirect(dec_host, app_id, con, &res);
- } else if (session && session->token) {
- if (embedded) {
- if (!strcmp(url, "/") && !strcmp(method, "GET")) {
- int fd = open(container->index, O_RDONLY);
- struct stat sb;
- fstat(fd, &sb);
- char html[sb.st_size + 1];
- read(fd, html, sb.st_size);
- close(fd);
- const size_t index_len = sb.st_size
- - strlen("%s") * 4 + key_len + host_len
- + app_url_len * 2;
- char index[index_len + 1];
- sprintf(index, html, key, host, app_url,
- app_url);
- res = MHD_create_response_from_buffer(index_len,
- index, MHD_RESPMEM_MUST_COPY);
+ } else if (session_token) {
+ int i = 0;
+ const struct shopify_api *api;
+ while ((api = &(container->apis[i++])))
+ if (!strcmp(url, api->url)
+ && !strcmp(method, api->method)) {
+ char *json = NULL;
+ api->cb(api->arg, session, json);
+ res = MHD_create_response_from_buffer(
+ strlen(json), json,
+ MHD_RESPMEM_MUST_FREE);
MHD_add_response_header(res,
"Content-Security-Policy",
header);
+ MHD_add_response_header(res, "Content-Type",
+ "application/json");
ret = MHD_queue_response(con, MHD_HTTP_OK, res);
- } else {
- int i = 0;
- const struct shopify_api *api;
- while ((api = &(container->apis[i++])))
- if (!strcmp(url, api->url)
- && !strcmp(method,
- api->method)) {
- char *json = NULL;
- api->cb(api->arg, session,
- json);
- res = MHD_create_response_from_buffer(
- strlen(json),
- json,
- MHD_RESPMEM_MUST_FREE);
- MHD_add_response_header(res,
- "Content-"\
- "Security-"\
- "Policy",
- header);
- MHD_add_response_header(res,
- "Content-"\
- "Type",
- "application/"\
- "json");
- ret = MHD_queue_response(con,
- MHD_HTTP_OK,
- res);
- break;
- }
+ break;
}
+ } else if (session && session->access_token) {
+ if (embedded) {
+ int fd = open(container->index, O_RDONLY);
+ struct stat sb;
+ fstat(fd, &sb);
+ char html[sb.st_size + 1];
+ read(fd, html, sb.st_size);
+ close(fd);
+ const size_t index_len = sb.st_size - strlen("%s") * 4
+ + key_len + host_len + app_url_len * 2;
+ char index[index_len + 1];
+ sprintf(index, html, key, host, app_url, app_url);
+ res = MHD_create_response_from_buffer(index_len, index,
+ MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(res, "Content-Security-Policy",
+ header);
+ ret = MHD_queue_response(con, MHD_HTTP_OK, res);
} else
ret = redirect(dec_host, app_id, con, &res);
} else {
@@ -367,8 +418,8 @@ void shopify_app(const char *api_key, const char *api_secret_key,
while (sessions[i].shop) {
if (sessions[i].scope)
free(sessions[i].scope);
- if (sessions[i].token)
- free(sessions[i].token);
+ if (sessions[i].access_token)
+ free(sessions[i].access_token);
free(sessions[i].nonce);
free(sessions[i++].shop);
}