summaryrefslogtreecommitdiff
path: root/rest
diff options
context:
space:
mode:
authorGünther Wagner <info@gunibert.de>2022-01-12 18:26:38 +0000
committerGünther Wagner <info@gunibert.de>2022-01-12 18:26:38 +0000
commit6d65d8862ad7d39e55be0adb868c05f194250e74 (patch)
tree46ffe23af55fb90e3199b184b9c820e0f8af0c9a /rest
parent7766d718735270d1c0850685a3c7655a5726d60f (diff)
downloadlibrest-6d65d8862ad7d39e55be0adb868c05f194250e74.tar.gz
OAuth2 Pkce Workflow
Diffstat (limited to 'rest')
-rw-r--r--rest/Makefile.am139
-rw-r--r--rest/meson.build28
-rw-r--r--rest/oauth2-proxy-call.c68
-rw-r--r--rest/oauth2-proxy-call.h49
-rw-r--r--rest/oauth2-proxy.c397
-rw-r--r--rest/oauth2-proxy.h95
-rw-r--r--rest/rest-oauth2-proxy-call.c63
-rw-r--r--rest/rest-oauth2-proxy-call.h (renamed from rest/oauth2-proxy-private.h)29
-rw-r--r--rest/rest-oauth2-proxy.c802
-rw-r--r--rest/rest-oauth2-proxy.h101
-rw-r--r--rest/rest-pkce-code-challenge.c121
-rw-r--r--rest/rest-pkce-code-challenge.h44
-rw-r--r--rest/rest-proxy-call.c18
-rw-r--r--rest/rest-proxy.c4
-rw-r--r--rest/rest-utils.c44
-rw-r--r--rest/rest-utils.h27
-rw-r--r--rest/rest.h35
17 files changed, 1287 insertions, 777 deletions
diff --git a/rest/Makefile.am b/rest/Makefile.am
deleted file mode 100644
index 64205d4..0000000
--- a/rest/Makefile.am
+++ /dev/null
@@ -1,139 +0,0 @@
-CLEANFILES =
-
-# For some reason I can't use $(librest_@API_VERSION@_la_SOURCES) in
-# test_runner_SOURCES, so we have to do this
-lib_sources = \
- rest-param.c \
- rest-params.c \
- rest-proxy.c \
- rest-proxy-auth.c \
- rest-proxy-auth-private.h \
- rest-proxy-call.c \
- rest-proxy-call-private.h \
- rest-xml-node.c \
- rest-xml-parser.c \
- rest-main.c \
- rest-private.h \
- rest-enum-types.c \
- oauth-proxy.c \
- oauth-proxy-call.c \
- oauth-proxy-private.h \
- oauth2-proxy.c \
- oauth2-proxy-call.c \
- oauth2-proxy-private.h \
- sha1.c \
- sha1.h
-nodist_lib_sources = \
- rest-marshal.h \
- rest-marshal.c
-lib_headers = \
- rest-param.h \
- rest-params.h \
- rest-proxy.h \
- rest-proxy-auth.h \
- rest-proxy-call.h \
- rest-enum-types.h \
- oauth-proxy.h \
- oauth-proxy-call.h \
- oauth2-proxy.h \
- oauth2-proxy-call.h \
- rest-xml-node.h \
- rest-xml-parser.h
-
-EXTRA_DIST = \
- rest-marshal.txt
-
-lib_LTLIBRARIES = librest-@API_VERSION@.la
-librest_@API_VERSION@_la_CFLAGS = $(GLIB_CFLAGS) $(GTHREAD_CFLAGS) \
- $(SOUP_CFLAGS) $(SOUP_GNOME_CFLAGS) \
- $(XML_CFLAGS) $(GCOV_CFLAGS) \
- -I$(top_srcdir) -Wall -DG_LOG_DOMAIN=\"Rest\"
-librest_@API_VERSION@_la_LDFLAGS = -no-undefined
-librest_@API_VERSION@_la_LIBADD = $(GLIB_LIBS) $(GTHREAD_LIBS) \
- $(SOUP_LIBS) $(SOUP_GNOME_LIBS) $(XML_LIBS) \
- $(GCOV_LDFLAGS)
-librest_@API_VERSION@_la_SOURCES = $(lib_sources) $(lib_headers)
-nodist_librest_@API_VERSION@_la_SOURCES = $(nodist_lib_sources)
-librest_@API_VERSION@_la_HEADERS = $(lib_headers)
-librest_@API_VERSION@_ladir = $(includedir)/rest-@API_VERSION@/rest
-
-rest-enum-types.h: stamp-rest-enum-types.h
- @true
-
-stamp-rest-enum-types.h: rest-proxy.h rest-proxy-call.h Makefile
- $(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) \
- --fhead "#ifndef __REST_ENUM_TYPES_H__\n#define __REST_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
- --fprod "/* enumerations from \"@filename@\" */\n" \
- --vhead "GType @enum_name@_get_type (void) G_GNUC_CONST;\n#define REST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
- --ftail "G_END_DECLS\n\n#endif /* __REST_ENUM_TYPES_H__ */" rest-proxy.h rest-proxy-call.h) >> xgen-gtbh \
- && (cmp -s xgen-gtbh rest-enum-types.h || cp xgen-gtbh rest-enum-types.h ) \
- && rm -f xgen-gtbh \
- && echo timestamp > $(@F)
-
-rest-enum-types.c: rest-proxy.h rest-proxy-call.h Makefile rest-enum-types.h
- $(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) \
- --fhead "#include \"rest-proxy.h\"\n#include \"rest-proxy-call.h\"\n#include \"rest-enum-types.h\"" \
- --fprod "\n/* enumerations from \"@filename@\" */" \
- --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \
- --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
- --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
- rest-proxy.h rest-proxy-call.h) > xgen-gtbc \
- && cp xgen-gtbc rest-enum-types.c \
- && rm -f xgen-gtbc
-
-BUILT_SOURCES = rest-marshal.c rest-marshal.h
-
-rest-marshal.c: rest-marshal.txt
- $(AM_V_GEN)echo "#include \"rest-marshal.h\"" > $@ && \
- glib-genmarshal --body $< >> $@ || (rm -f $@ && exit 1)
-
-rest-marshal.h: rest-marshal.txt
- $(AM_V_GEN)glib-genmarshal --header $< > $@ || (rm -f $@ && exit 1)
-
-
-# Test suite
-TESTS = test-runner
-check_PROGRAMS = test-runner
-
-test_runner_SOURCES = test-runner.c $(lib_sources) $(nodist_lib_sources) $(lib_headers)
-test_runner_CFLAGS = -DBUILD_TESTS $(librest_@API_VERSION@_la_CFLAGS) $(GCOV_CFLAGS)
-test_runner_LDFLAGS = $(librest_@API_VERSION@_la_LIBADD) $(GCOV_LDFLAGS)
-
-# TODO: use gtester
-
-# introspection
--include $(INTROSPECTION_MAKEFILE)
-
-if HAVE_INTROSPECTION
-INTROSPECTION_GIRS = Rest-@API_VERSION@.gir
-
-Rest-@API_VERSION@.gir: librest-@API_VERSION@.la Makefile
-
-Rest_@API_VERSION_AM@_gir_NAMESPACE = Rest
-Rest_@API_VERSION_AM@_gir_VERSION = @API_VERSION@
-Rest_@API_VERSION_AM@_gir_LIBS = librest-@API_VERSION@.la
-Rest_@API_VERSION_AM@_gir_FILES = \
- $(lib_headers) \
- $(filter-out %private.h, $(lib_sources))
-Rest_@API_VERSION_AM@_gir_CFLAGS = -I$(top_srcdir)
-Rest_@API_VERSION_AM@_gir_INCLUDES = GObject-2.0 Gio-2.0 Soup-2.4
-Rest_@API_VERSION_AM@_gir_PACKAGES = gobject-2.0 libsoup-2.4 libxml-2.0 gio-2.0
-Rest_@API_VERSION_AM@_gir_SCANNERFLAGS = --accept-unprefixed --warn-all
-Rest_@API_VERSION_AM@_gir_EXPORT_PACKAGES = rest-@API_VERSION@
-REST_CINCLUDES=$(patsubst %,--c-include='rest/%',$(shell echo $(lib_headers)))
-INTROSPECTION_SCANNER_ARGS = $(REST_CINCLUDES)
-
-girdir = $(datadir)/gir-1.0
-dist_gir_DATA = $(INTROSPECTION_GIRS)
-
-typelibsdir = $(libdir)/girepository-1.0/
-typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-
-CLEANFILES += $(dist_gir_DATA) \
- $(typelibs_DATA) \
- rest-enum-types.h \
- rest-enum-types.c \
- stamp-rest-enum-types.h \
- $(NULL)
-
-endif # HAVE_INTROSPECTION
diff --git a/rest/meson.build b/rest/meson.build
index 396ec83..a22742e 100644
--- a/rest/meson.build
+++ b/rest/meson.build
@@ -19,10 +19,13 @@ librest_sources = [
'rest-main.c',
'oauth-proxy.c',
'oauth-proxy-call.c',
- 'oauth2-proxy.c',
- 'oauth2-proxy-call.c',
'sha1.c',
+ 'rest-oauth2-proxy.c',
+ 'rest-oauth2-proxy-call.c',
+ 'rest-pkce-code-challenge.c',
+ 'rest-utils.c',
+
librest_enums,
librest_marshal,
]
@@ -30,8 +33,6 @@ librest_sources = [
librest_headers = [
'oauth-proxy-call.h',
'oauth-proxy.h',
- 'oauth2-proxy-call.h',
- 'oauth2-proxy.h',
'rest-param.h',
'rest-params.h',
'rest-proxy-auth.h',
@@ -39,11 +40,18 @@ librest_headers = [
'rest-proxy.h',
'rest-xml-node.h',
'rest-xml-parser.h',
+
+ 'rest-oauth2-proxy.h',
+ 'rest-oauth2-proxy-call.h',
+ 'rest-pkce-code-challenge.h',
+ 'rest-utils.h',
+ 'rest.h',
]
librest_deps = [
glib_dep,
libsoup_dep,
+ libjson_glib_dep,
libxml_dep,
]
@@ -65,6 +73,8 @@ install_headers(librest_headers,
subdir: librest_pkg_string / 'rest',
)
+rest_dep_sources = []
+
# GObject Introspection
if get_option('introspection')
librest_gir_extra_args = [
@@ -77,17 +87,25 @@ if get_option('introspection')
endforeach
librest_gir = gnome.generate_gir(librest_lib,
- sources: [ librest_headers, librest_enums[1] ],
+ sources: [ librest_headers, librest_sources, librest_enums[1] ],
+ dependencies: librest_deps,
namespace: 'Rest',
+ identifier_prefix: 'Rest',
+ symbol_prefix: 'rest',
nsversion: librest_api_version,
includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-@0@'.format(libsoup_api_version) ],
extra_args: librest_gir_extra_args,
install: true,
)
+
+ rest_dep_sources += librest_gir
endif
librest_dep = declare_dependency(
+ sources: rest_dep_sources,
link_with: librest_lib,
+ include_directories: include_directories('..'),
+ dependencies: librest_deps,
)
# Test suite
diff --git a/rest/oauth2-proxy-call.c b/rest/oauth2-proxy-call.c
deleted file mode 100644
index 2d71b45..0000000
--- a/rest/oauth2-proxy-call.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * librest - RESTful web services access
- * Copyright (c) 2008, 2009, Intel Corporation.
- *
- * Authors: Rob Bradford <rob@linux.intel.com>
- * Ross Burton <ross@linux.intel.com>
- * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <string.h>
-#include <libsoup/soup.h>
-#include <rest/rest-proxy-call.h>
-#include "oauth2-proxy-call.h"
-#include "oauth2-proxy-private.h"
-#include "sha1.h"
-
-G_DEFINE_TYPE (OAuth2ProxyCall, oauth2_proxy_call, REST_TYPE_PROXY_CALL)
-
-static gboolean
-_prepare (RestProxyCall *call, GError **error)
-{
- OAuth2Proxy *proxy = NULL;
- gboolean result = TRUE;
-
- g_object_get (call, "proxy", &proxy, NULL);
-
- if (!proxy->priv->access_token) {
- g_set_error (error,
- REST_PROXY_CALL_ERROR,
- REST_PROXY_CALL_FAILED,
- "Missing access token, web service not properly authenticated");
- result = FALSE;
- } else {
- rest_proxy_call_add_param (call, "access_token", proxy->priv->access_token);
- }
-
- g_object_unref (proxy);
-
- return result;
-}
-
-static void
-oauth2_proxy_call_class_init (OAuth2ProxyCallClass *klass)
-{
- RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass);
-
- call_class->prepare = _prepare;
-}
-
-static void
-oauth2_proxy_call_init (OAuth2ProxyCall *self)
-{
-}
-
diff --git a/rest/oauth2-proxy-call.h b/rest/oauth2-proxy-call.h
deleted file mode 100644
index 805a501..0000000
--- a/rest/oauth2-proxy-call.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * librest - RESTful web services access
- * Copyright (c) 2008, 2009, Intel Corporation.
- *
- * Authors: Rob Bradford <rob@linux.intel.com>
- * Ross Burton <ross@linux.intel.com>
- * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef _OAUTH2_PROXY_CALL
-#define _OAUTH2_PROXY_CALL
-
-#include <rest/rest-proxy-call.h>
-
-G_BEGIN_DECLS
-
-#define OAUTH2_TYPE_PROXY_CALL oauth2_proxy_call_get_type()
-G_DECLARE_DERIVABLE_TYPE (OAuth2ProxyCall, oauth2_proxy_call, OAUTH2, PROXY_CALL, RestProxyCall)
-
-/**
- * OAuth2ProxyCall:
- *
- * #OAuth2ProxyCall has no publicly available members.
- */
-
-struct _OAuth2ProxyCallClass {
- RestProxyCallClass parent_class;
- /*< private >*/
- /* padding for future expansion */
- gpointer _padding_dummy[8];
-};
-
-G_END_DECLS
-
-#endif /* _OAUTH2_PROXY_CALL */
diff --git a/rest/oauth2-proxy.c b/rest/oauth2-proxy.c
deleted file mode 100644
index 3382f8b..0000000
--- a/rest/oauth2-proxy.c
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * librest - RESTful web services access
- * Copyright (c) 2008, 2009, 2010 Intel Corporation.
- *
- * Authors: Rob Bradford <rob@linux.intel.com>
- * Ross Burton <ross@linux.intel.com>
- * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <rest/rest-proxy.h>
-#include <libsoup/soup.h>
-#include "oauth2-proxy.h"
-#include "oauth2-proxy-private.h"
-#include "oauth2-proxy-call.h"
-
-#define GET_PRIVATE(o) oauth2_proxy_get_instance_private(OAUTH2_PROXY(o))
-
-G_DEFINE_TYPE_WITH_PRIVATE (OAuth2Proxy, oauth2_proxy, REST_TYPE_PROXY)
-
-GQuark
-oauth2_proxy_error_quark (void)
-{
- return g_quark_from_static_string ("rest-oauth2-proxy");
-}
-
-enum {
- PROP_0,
- PROP_CLIENT_ID,
- PROP_AUTH_ENDPOINT,
- PROP_ACCESS_TOKEN
-};
-
-static RestProxyCall *
-_new_call (RestProxy *proxy)
-{
- RestProxyCall *call;
-
- call = g_object_new (OAUTH2_TYPE_PROXY_CALL,
- "proxy", proxy,
- NULL);
-
- return call;
-}
-
-static void
-oauth2_proxy_get_property (GObject *object, guint property_id,
- GValue *value, GParamSpec *pspec)
-{
- OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv;
-
- switch (property_id) {
- case PROP_CLIENT_ID:
- g_value_set_string (value, priv->client_id);
- break;
- case PROP_AUTH_ENDPOINT:
- g_value_set_string (value, priv->auth_endpoint);
- break;
- case PROP_ACCESS_TOKEN:
- g_value_set_string (value, priv->access_token);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-oauth2_proxy_set_property (GObject *object, guint property_id,
- const GValue *value, GParamSpec *pspec)
-{
- OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv;
-
- switch (property_id) {
- case PROP_CLIENT_ID:
- if (priv->client_id)
- g_free (priv->client_id);
- priv->client_id = g_value_dup_string (value);
- break;
- case PROP_AUTH_ENDPOINT:
- if (priv->auth_endpoint)
- g_free (priv->auth_endpoint);
- priv->auth_endpoint = g_value_dup_string (value);
- break;
- case PROP_ACCESS_TOKEN:
- if (priv->access_token)
- g_free (priv->access_token);
- priv->access_token = g_value_dup_string (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-oauth2_proxy_finalize (GObject *object)
-{
- OAuth2ProxyPrivate *priv = ((OAuth2Proxy*)object)->priv;
-
- g_free (priv->client_id);
- g_free (priv->auth_endpoint);
- g_free (priv->access_token);
-
- G_OBJECT_CLASS (oauth2_proxy_parent_class)->finalize (object);
-}
-
-static void
-oauth2_proxy_class_init (OAuth2ProxyClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- RestProxyClass *proxy_class = REST_PROXY_CLASS (klass);
- GParamSpec *pspec;
-
- object_class->get_property = oauth2_proxy_get_property;
- object_class->set_property = oauth2_proxy_set_property;
- object_class->finalize = oauth2_proxy_finalize;
-
- proxy_class->new_call = _new_call;
-
- pspec = g_param_spec_string ("client-id", "client-id",
- "The client (application) id", NULL,
- G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class,
- PROP_CLIENT_ID,
- pspec);
-
- pspec = g_param_spec_string ("auth-endpoint", "auth-endpoint",
- "The authentication endpoint url", NULL,
- G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY|G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class,
- PROP_AUTH_ENDPOINT,
- pspec);
-
- pspec = g_param_spec_string ("access-token", "access-token",
- "The request or access token", NULL,
- G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
- g_object_class_install_property (object_class,
- PROP_ACCESS_TOKEN,
- pspec);
-}
-
-static void
-oauth2_proxy_init (OAuth2Proxy *proxy)
-{
- proxy->priv = GET_PRIVATE (proxy);
-}
-
-/**
- * oauth2_proxy_new:
- * @client_id: the client (application) id
- * @auth_endpoint: the authentication endpoint URL
- * @url_format: the endpoint URL
- * @binding_required: whether the URL needs to be bound before calling
- *
- * Create a new #OAuth2Proxy for the specified endpoint @url_format, using the
- * specified API key and secret.
- *
- * This proxy won't have the Token set so will be unauthorised. If the token is
- * unknown then the following steps should be taken to acquire an access token:
- * - Get the authentication url with oauth2_proxy_build_login_url()
- * - Display this url in an embedded browser widget
- * - wait for the browser widget to be redirected to the specified redirect_uri
- * - extract the token from the fragment of the redirected uri (using
- * convenience function oauth2_proxy_extract_access_token())
- * - set the token with oauth2_proxy_set_access_token()
- *
- * Set @binding_required to %TRUE if the URL contains string formatting
- * operations (for example "http://foo.com/%<!-- -->s". These must be expanded
- * using rest_proxy_bind() before invoking the proxy.
- *
- * Returns: A new #OAuth2Proxy.
- */
-RestProxy *
-oauth2_proxy_new (const char *client_id,
- const char *auth_endpoint,
- const gchar *url_format,
- gboolean binding_required)
-{
- return g_object_new (OAUTH2_TYPE_PROXY,
- "client-id", client_id,
- "auth-endpoint", auth_endpoint,
- "url-format", url_format,
- "binding-required", binding_required,
- NULL);
-}
-
-/**
- * oauth2_proxy_new_with_token:
- * @client_id: the client (application) id
- * @access_token: the Access Token
- * @auth_endpoint: the authentication endpoint URL
- * @url_format: the endpoint URL
- * @binding_required: whether the URL needs to be bound before calling
- *
- * Create a new #OAuth2Proxy for the specified endpoint @url_format, using the
- * specified client id
- *
- * @access_token is used for the Access Token, so if they are still valid then
- * this proxy is authorised.
- *
- * Set @binding_required to %TRUE if the URL contains string formatting
- * operations (for example "http://foo.com/%<!-- -->s". These must be expanded
- * using rest_proxy_bind() before invoking the proxy.
- *
- * Returns: A new #OAuth2Proxy.
- */
-RestProxy *
-oauth2_proxy_new_with_token (const char *client_id,
- const char *access_token,
- const char *auth_endpoint,
- const gchar *url_format,
- gboolean binding_required)
-{
- return g_object_new (OAUTH2_TYPE_PROXY,
- "client-id", client_id,
- "access-token", access_token,
- "auth-endpoint", auth_endpoint,
- "url-format", url_format,
- "binding-required", binding_required,
- NULL);
-}
-
-/* allocates a new string of the form "key=value" */
-static void
-append_query_param (gpointer key, gpointer value, gpointer user_data)
-{
- GString *params = (GString*) user_data;
- char *encoded_val, *encoded_key;
- char *param;
-
- encoded_val = g_uri_escape_string (value, NULL, TRUE);
- encoded_key = g_uri_escape_string (key, NULL, TRUE);
-
- param = g_strdup_printf ("%s=%s", encoded_key, encoded_val);
- g_free (encoded_key);
- g_free (encoded_val);
-
- // if there's already a parameter in the string, we need to add a '&'
- // separator before adding the new param
- if (params->len)
- g_string_append_c (params, '&');
- g_string_append (params, param);
-}
-
-/**
- * oauth2_proxy_build_login_url_full:
- * @proxy: a OAuth2Proxy object
- * @redirect_uri: the uri to redirect to after the user authenticates
- * @extra_params: any extra parameters to add to the login url (e.g. facebook
- * uses 'scope=foo,bar' to request extended permissions).
- *
- * Builds a url at which the user can log in to the specified OAuth2-based web
- * service. In general, this url should be displayed in an embedded browser
- * widget, and you should then intercept the browser's redirect to @redirect_uri
- * and extract the access token from the url fragment. After the access token
- * has been retrieved, call oauth2_proxy_set_access_token(). This must be done
- * before making any API calls to the service.
- *
- * See the oauth2 spec for more details about the "user-agent" authentication
- * flow.
- *
- * The @extra_params and @redirect_uri should not be uri-encoded, that will be
- * done automatically
- *
- * Returns: a newly allocated uri string
- */
-char *
-oauth2_proxy_build_login_url_full (OAuth2Proxy *proxy,
- const char* redirect_uri,
- GHashTable* extra_params)
-{
- char *url;
- GString *params = 0;
- char *encoded_uri, *encoded_id;
-
- g_return_val_if_fail (proxy, NULL);
- g_return_val_if_fail (redirect_uri, NULL);
-
- if (extra_params && g_hash_table_size (extra_params) > 0) {
- params = g_string_new (NULL);
- g_hash_table_foreach (extra_params, append_query_param, params);
- }
-
- encoded_uri = g_uri_escape_string (redirect_uri, NULL, TRUE);
- encoded_id = g_uri_escape_string (proxy->priv->client_id, NULL, TRUE);
-
- url = g_strdup_printf ("%s?client_id=%s&redirect_uri=%s&type=user_agent",
- proxy->priv->auth_endpoint, encoded_id,
- encoded_uri);
-
- g_free (encoded_uri);
- g_free (encoded_id);
-
- if (params) {
- char * full_url = g_strdup_printf ("%s&%s", url, params->str);
- g_free (url);
- url = full_url;
- g_string_free (params, TRUE);
- }
-
- return url;
-}
-
-/**
- * oauth2_proxy_build_login_url:
- * @proxy: an OAuth2Proxy object
- * @redirect_uri: the uri to redirect to after the user authenticates
- *
- * Builds a url at which the user can log in to the specified OAuth2-based web
- * service. See the documentation for oauth2_proxy_build_login_url_full() for
- * detailed information.
- *
- * Returns: a newly allocated uri string
- */
-char *
-oauth2_proxy_build_login_url (OAuth2Proxy *proxy,
- const char* redirect_uri)
-{
- return oauth2_proxy_build_login_url_full (proxy, redirect_uri, NULL);
-}
-
-/**
- * oauth2_proxy_get_access_token:
- * @proxy: an #OAuth2Proxy
- *
- * Get the current request or access token.
- *
- * Returns: the token, or %NULL if there is no token yet. This string is owned
- * by #OAuth2Proxy and should not be freed.
- */
-const char *
-oauth2_proxy_get_access_token (OAuth2Proxy *proxy)
-{
- return proxy->priv->access_token;
-}
-
-/**
- * oauth2_proxy_set_access_token:
- * @proxy: an #OAuth2Proxy
- * @access_token: the access token
- *
- * Set the access token.
- */
-void
-oauth2_proxy_set_access_token (OAuth2Proxy *proxy, const char *access_token)
-{
- g_return_if_fail (OAUTH2_IS_PROXY (proxy));
-
- if (proxy->priv->access_token)
- g_free (proxy->priv->access_token);
-
- proxy->priv->access_token = g_strdup (access_token);
-}
-
-/**
- * oauth2_proxy_extract_access_token:
- * @url: the url which contains an access token in its fragment
- *
- * A utility function to extract the access token from the url that results from
- * the redirection after the user authenticates
- */
-char *
-oauth2_proxy_extract_access_token (const char *url)
-{
- GHashTable *params;
- char *token = NULL;
- const char *fragment;
- GUri *uri = g_uri_parse (url, G_URI_FLAGS_ENCODED, NULL);
-
- fragment = g_uri_get_fragment (uri);
- if (fragment != NULL) {
- params = soup_form_decode (fragment);
-
- if (params) {
- char *encoded = g_hash_table_lookup (params, "access_token");
- if (encoded)
- token = g_uri_unescape_string (encoded, NULL);
-
- g_hash_table_destroy (params);
- }
- }
- g_uri_unref (uri);
-
- return token;
-}
diff --git a/rest/oauth2-proxy.h b/rest/oauth2-proxy.h
deleted file mode 100644
index 0473c62..0000000
--- a/rest/oauth2-proxy.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * librest - RESTful web services access
- * Copyright (c) 2008, 2009, 2010 Intel Corporation.
- *
- * Authors: Rob Bradford <rob@linux.intel.com>
- * Ross Burton <ross@linux.intel.com>
- * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef _OAUTH2_PROXY
-#define _OAUTH2_PROXY
-
-#include <rest/rest-proxy.h>
-
-G_BEGIN_DECLS
-
-#define OAUTH2_TYPE_PROXY oauth2_proxy_get_type()
-
-#define OAUTH2_PROXY(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST ((obj), OAUTH2_TYPE_PROXY, OAuth2Proxy))
-
-#define OAUTH2_PROXY_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST ((klass), OAUTH2_TYPE_PROXY, OAuth2ProxyClass))
-
-#define OAUTH2_IS_PROXY(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OAUTH2_TYPE_PROXY))
-
-#define OAUTH2_IS_PROXY_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE ((klass), OAUTH2_TYPE_PROXY))
-
-#define OAUTH2_PROXY_GET_CLASS(obj) \
- (G_TYPE_INSTANCE_GET_CLASS ((obj), OAUTH2_TYPE_PROXY, OAuth2ProxyClass))
-
-typedef struct _OAuth2ProxyPrivate OAuth2ProxyPrivate;
-
-/**
- * OAuth2Proxy:
- *
- * #OAuth2Proxy has no publicly available members.
- */
-typedef struct {
- RestProxy parent;
- OAuth2ProxyPrivate *priv;
-} OAuth2Proxy;
-
-typedef struct {
- RestProxyClass parent_class;
- /*< private >*/
- /* padding for future expansion */
- gpointer _padding_dummy[8];
-} OAuth2ProxyClass;
-
-GType oauth2_proxy_get_type (void);
-
-RestProxy* oauth2_proxy_new (const char *client_id,
- const char *auth_endpoint,
- const gchar *url_format,
- gboolean binding_required);
-
-RestProxy* oauth2_proxy_new_with_token (const char *client_id,
- const char *access_token,
- const char *auth_endpoint,
- const gchar *url_format,
- gboolean binding_required);
-
-char * oauth2_proxy_build_login_url_full (OAuth2Proxy *proxy,
- const char* redirect_uri,
- GHashTable* extra_params);
-
-char * oauth2_proxy_build_login_url (OAuth2Proxy *proxy,
- const char* redirect_uri);
-
-const char * oauth2_proxy_get_access_token (OAuth2Proxy *proxy);
-
-void oauth2_proxy_set_access_token (OAuth2Proxy *proxy, const char *access_token);
-
-char * oauth2_proxy_extract_access_token (const char *url);
-
-G_END_DECLS
-
-#endif /* _OAUTH2_PROXY */
diff --git a/rest/rest-oauth2-proxy-call.c b/rest/rest-oauth2-proxy-call.c
new file mode 100644
index 0000000..cbea6f5
--- /dev/null
+++ b/rest/rest-oauth2-proxy-call.c
@@ -0,0 +1,63 @@
+/* rest-oauth2-proxy-call.c
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rest-oauth2-proxy-call.h"
+
+G_DEFINE_TYPE (RestOAuth2ProxyCall, rest_oauth2_proxy_call, REST_TYPE_PROXY_CALL)
+
+static gboolean
+rest_oauth2_proxy_call_prepare (RestProxyCall *call,
+ GError **error)
+{
+ RestOAuth2ProxyCall *self = (RestOAuth2ProxyCall *)call;
+ RestOAuth2Proxy *proxy = NULL;
+ g_autoptr(GDateTime) now = NULL;
+ GDateTime *expiration_date = NULL;
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY_CALL (call), FALSE);
+
+ g_object_get (call, "proxy", &proxy, NULL);
+
+ now = g_date_time_new_now_local ();
+ expiration_date = rest_oauth2_proxy_get_expiration_date (proxy);
+
+ // access token expired -> refresh
+ if (g_date_time_compare (now, expiration_date) > 0)
+ {
+ g_set_error (error,
+ REST_OAUTH2_ERROR,
+ REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED,
+ "Access token is expired");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+rest_oauth2_proxy_call_class_init (RestOAuth2ProxyCallClass *klass)
+{
+ RestProxyCallClass *call_class = REST_PROXY_CALL_CLASS (klass);
+
+ call_class->prepare = rest_oauth2_proxy_call_prepare;
+}
+
+static void
+rest_oauth2_proxy_call_init (RestOAuth2ProxyCall *self)
+{
+}
diff --git a/rest/oauth2-proxy-private.h b/rest/rest-oauth2-proxy-call.h
index 565c1c3..fafc61f 100644
--- a/rest/oauth2-proxy-private.h
+++ b/rest/rest-oauth2-proxy-call.h
@@ -1,10 +1,6 @@
-/*
- * librest - RESTful web services access
- * Copyright (c) 2008, 2009, 2010 Intel Corporation.
+/* rest-oauth2-proxy-call.h
*
- * Authors: Rob Bradford <rob@linux.intel.com>
- * Ross Burton <ross@linux.intel.com>
- * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
@@ -18,17 +14,20 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
-#ifndef _OAUTH2_PROXY_PRIVATE
-#define _OAUTH2_PROXY_PRIVATE
-#include "oauth2-proxy.h"
+#pragma once
+
+#include <rest.h>
+
+G_BEGIN_DECLS
+
+#define REST_TYPE_OAUTH2_PROXY_CALL (rest_oauth2_proxy_call_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (RestOAuth2ProxyCall, rest_oauth2_proxy_call, REST, OAUTH2_PROXY_CALL, RestProxyCall)
-struct _OAuth2ProxyPrivate {
- char *client_id;
- char *auth_endpoint;
- char *access_token;
+struct _RestOAuth2ProxyCallClass {
+ RestProxyCallClass parent_class;
};
-#endif /* _OAUTH2_PROXY_PRIVATE */
+G_END_DECLS
diff --git a/rest/rest-oauth2-proxy.c b/rest/rest-oauth2-proxy.c
new file mode 100644
index 0000000..f15b589
--- /dev/null
+++ b/rest/rest-oauth2-proxy.c
@@ -0,0 +1,802 @@
+/* rest-oauth2-proxy.c
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rest-oauth2-proxy.h"
+#include "rest-oauth2-proxy-call.h"
+#include "rest-utils.h"
+#include "rest-private.h"
+#include <json-glib/json-glib.h>
+
+typedef struct
+{
+ gchar *authurl;
+ gchar *tokenurl;
+ gchar *redirect_uri;
+ gchar *client_id;
+ gchar *client_secret;
+
+ gchar *access_token;
+ gchar *refresh_token;
+
+ GDateTime *expiration_date;
+} RestOAuth2ProxyPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (RestOAuth2Proxy, rest_oauth2_proxy, REST_TYPE_PROXY)
+
+G_DEFINE_QUARK (rest-oauth2-error-quark, rest_oauth2_error)
+
+enum {
+ PROP_0,
+ PROP_AUTH_URL,
+ PROP_TOKEN_URL,
+ PROP_REDIRECT_URI,
+ PROP_CLIENT_ID,
+ PROP_CLIENT_SECRET,
+ PROP_ACCESS_TOKEN,
+ PROP_REFRESH_TOKEN,
+ PROP_EXPIRATION_DATE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+rest_oauth2_proxy_parse_access_token (RestOAuth2Proxy *self,
+ GBytes *payload,
+ GTask *task)
+{
+ g_autoptr(JsonParser) parser = NULL;
+ g_autoptr(GError) error = NULL;
+ JsonNode *root;
+ JsonObject *root_object;
+ const gchar *data;
+ gsize size;
+ gint expires_in;
+ gint created_at;
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ data = g_bytes_get_data (payload, &size);
+
+ parser = json_parser_new ();
+ json_parser_load_from_data (parser, data, size, &error);
+ if (error != NULL)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ root = json_parser_get_root (parser);
+ root_object = json_node_get_object (root);
+
+ if (json_object_has_member (root_object, "access_token"))
+ rest_oauth2_proxy_set_access_token (self, json_object_get_string_member (root_object, "access_token"));
+ if (json_object_has_member (root_object, "refresh_token"))
+ rest_oauth2_proxy_set_refresh_token (self, json_object_get_string_member (root_object, "refresh_token"));
+
+ if (json_object_has_member (root_object, "expires_in") && json_object_has_member (root_object, "created_at"))
+ {
+ expires_in = json_object_get_int_member (root_object, "expires_in");
+ created_at = json_object_get_int_member (root_object, "created_at");
+
+ rest_oauth2_proxy_set_expiration_date (self, g_date_time_new_from_unix_local (created_at+expires_in));
+ }
+ else if (json_object_has_member (root_object, "expires_in"))
+ {
+ g_autoptr(GDateTime) now = g_date_time_new_now_utc ();
+ expires_in = json_object_get_int_member (root_object, "expires_in");
+ rest_oauth2_proxy_set_expiration_date (self, g_date_time_add_seconds (now, expires_in));
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+RestProxyCall *
+rest_oauth2_proxy_new_call (RestProxy *proxy)
+{
+ RestOAuth2Proxy *self = (RestOAuth2Proxy *)proxy;
+ RestProxyCall *call;
+ g_autofree gchar *auth;
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ auth = g_strdup_printf ("Bearer %s", rest_oauth2_proxy_get_access_token (self));
+
+ call = g_object_new (REST_TYPE_OAUTH2_PROXY_CALL, "proxy", proxy, NULL);
+ rest_proxy_call_add_header (call, "Authorization", auth);
+
+ return call;
+}
+
+/**
+ * rest_oauth2_proxy_new:
+ *
+ * Create a new #RestOAuth2Proxy.
+ *
+ * Returns: (transfer full): a newly created #RestOAuth2Proxy
+ */
+RestOAuth2Proxy *
+rest_oauth2_proxy_new (const gchar *authurl,
+ const gchar *tokenurl,
+ const gchar *redirecturl,
+ const gchar *client_id,
+ const gchar *client_secret,
+ const gchar *baseurl)
+{
+ return g_object_new (REST_TYPE_OAUTH2_PROXY,
+ "url-format", baseurl,
+ "auth-url", authurl,
+ "token-url", tokenurl,
+ "redirect-uri", redirecturl,
+ "client-id", client_id,
+ "client-secret", client_secret,
+ NULL);
+}
+
+static void
+rest_oauth2_proxy_finalize (GObject *object)
+{
+ RestOAuth2Proxy *self = (RestOAuth2Proxy *)object;
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_clear_pointer (&priv->authurl, g_free);
+ g_clear_pointer (&priv->tokenurl, g_free);
+ g_clear_pointer (&priv->redirect_uri, g_free);
+ g_clear_pointer (&priv->client_id, g_free);
+ g_clear_pointer (&priv->client_secret, g_free);
+ g_clear_pointer (&priv->access_token, g_free);
+ g_clear_pointer (&priv->refresh_token, g_free);
+ g_clear_pointer (&priv->expiration_date, g_date_time_unref);
+
+ G_OBJECT_CLASS (rest_oauth2_proxy_parent_class)->finalize (object);
+}
+
+static void
+rest_oauth2_proxy_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RestOAuth2Proxy *self = REST_OAUTH2_PROXY (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTH_URL:
+ g_value_set_string (value, rest_oauth2_proxy_get_auth_url (self));
+ break;
+ case PROP_TOKEN_URL:
+ g_value_set_string (value, rest_oauth2_proxy_get_token_url (self));
+ break;
+ case PROP_REDIRECT_URI:
+ g_value_set_string (value, rest_oauth2_proxy_get_redirect_uri (self));
+ break;
+ case PROP_CLIENT_ID:
+ g_value_set_string (value, rest_oauth2_proxy_get_client_id (self));
+ break;
+ case PROP_CLIENT_SECRET:
+ g_value_set_string (value, rest_oauth2_proxy_get_client_secret (self));
+ break;
+ case PROP_ACCESS_TOKEN:
+ g_value_set_string (value, rest_oauth2_proxy_get_access_token (self));
+ break;
+ case PROP_REFRESH_TOKEN:
+ g_value_set_string (value, rest_oauth2_proxy_get_refresh_token (self));
+ break;
+ case PROP_EXPIRATION_DATE:
+ g_value_set_boxed (value, rest_oauth2_proxy_get_expiration_date (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+rest_oauth2_proxy_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RestOAuth2Proxy *self = REST_OAUTH2_PROXY (object);
+
+ switch (prop_id)
+ {
+ case PROP_AUTH_URL:
+ rest_oauth2_proxy_set_auth_url (self, g_value_get_string (value));
+ break;
+ case PROP_TOKEN_URL:
+ rest_oauth2_proxy_set_token_url (self, g_value_get_string (value));
+ break;
+ case PROP_REDIRECT_URI:
+ rest_oauth2_proxy_set_redirect_uri (self, g_value_get_string (value));
+ break;
+ case PROP_CLIENT_ID:
+ rest_oauth2_proxy_set_client_id (self, g_value_get_string (value));
+ break;
+ case PROP_CLIENT_SECRET:
+ rest_oauth2_proxy_set_client_secret (self, g_value_get_string (value));
+ break;
+ case PROP_ACCESS_TOKEN:
+ rest_oauth2_proxy_set_access_token (self, g_value_get_string (value));
+ break;
+ case PROP_REFRESH_TOKEN:
+ rest_oauth2_proxy_set_refresh_token (self, g_value_get_string (value));
+ break;
+ case PROP_EXPIRATION_DATE:
+ rest_oauth2_proxy_set_expiration_date (self, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+rest_oauth2_proxy_class_init (RestOAuth2ProxyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ RestOAuth2ProxyClass *oauth2_class = REST_OAUTH2_PROXY_CLASS (klass);
+ RestProxyClass *proxy_class = REST_PROXY_CLASS (klass);
+
+ object_class->finalize = rest_oauth2_proxy_finalize;
+ object_class->get_property = rest_oauth2_proxy_get_property;
+ object_class->set_property = rest_oauth2_proxy_set_property;
+ oauth2_class->parse_access_token = rest_oauth2_proxy_parse_access_token;
+ proxy_class->new_call = rest_oauth2_proxy_new_call;
+
+ properties [PROP_AUTH_URL] =
+ g_param_spec_string ("auth-url",
+ "AuthUrl",
+ "AuthUrl",
+ "",
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TOKEN_URL] =
+ g_param_spec_string ("token-url",
+ "TokenUrl",
+ "TokenUrl",
+ "",
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_REDIRECT_URI] =
+ g_param_spec_string ("redirect-uri",
+ "RedirectUri",
+ "RedirectUri",
+ "",
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CLIENT_ID] =
+ g_param_spec_string ("client-id",
+ "ClientId",
+ "ClientId",
+ "",
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CLIENT_SECRET] =
+ g_param_spec_string ("client-secret",
+ "ClientSecret",
+ "ClientSecret",
+ "",
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ACCESS_TOKEN] =
+ g_param_spec_string ("access-token",
+ "AccessToken",
+ "AccessToken",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_REFRESH_TOKEN] =
+ g_param_spec_string ("refresh-token",
+ "RefreshToken",
+ "RefreshToken",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_EXPIRATION_DATE] =
+ g_param_spec_boxed ("expiration-date",
+ "ExpirationDate",
+ "ExpirationDate",
+ G_TYPE_DATE_TIME,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+rest_oauth2_proxy_init (RestOAuth2Proxy *self)
+{
+}
+
+/**
+ * rest_oauth2_proxy_build_authorization_url:
+ * @self: a #RestOAuth2Proxy
+ * @code_challenge: the code challenge (see #RestPkceCodeChallenge)
+ * @scope: (nullable): the requesting scope of the resource
+ * @state: (out): a CRSF token which should be verified from the redirect_uri
+ *
+ *
+ * Returns: (transfer full): the authorization url which should be shown in a WebView in order to accept/decline the request
+ * to authorize the application
+ *
+ * Since: 0.8
+ */
+gchar *
+rest_oauth2_proxy_build_authorization_url (RestOAuth2Proxy *self,
+ const gchar *code_challenge,
+ const gchar *scope,
+ gchar **state)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+ g_autoptr(GHashTable) params = NULL;
+ g_autoptr(GUri) auth = NULL;
+ g_autoptr(GUri) authorization_url = NULL;
+ g_autofree gchar *params_string;
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ if (state != NULL)
+ *state = random_string (10);
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (params, "response_type", "code");
+ g_hash_table_insert (params, "client_id", priv->client_id);
+ g_hash_table_insert (params, "redirect_uri", priv->redirect_uri);
+ if (state != NULL)
+ g_hash_table_insert (params, "state", *state);
+ g_hash_table_insert (params, "code_challenge", (gchar *)code_challenge);
+ g_hash_table_insert (params, "code_challenge_method", "S256");
+ if (scope)
+ g_hash_table_insert (params, "scope", (gchar *)scope);
+
+ params_string = soup_form_encode_hash (params);
+ auth = g_uri_parse (priv->authurl, G_URI_FLAGS_NONE, NULL);
+ authorization_url = g_uri_build (G_URI_FLAGS_ENCODED,
+ g_uri_get_scheme (auth),
+ NULL,
+ g_uri_get_host (auth),
+ g_uri_get_port (auth),
+ g_uri_get_path (auth),
+ params_string,
+ NULL);
+ return g_uri_to_string (authorization_url);
+}
+
+static void
+rest_oauth2_proxy_fetch_access_token_cb (SoupMessage *msg,
+ GBytes *body,
+ GError *error,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = user_data;
+ RestOAuth2Proxy *self;
+
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+
+ if (error)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, body, g_steal_pointer (&task));
+}
+
+void
+rest_oauth2_proxy_fetch_access_token_async (RestOAuth2Proxy *self,
+ const gchar *authorization_code,
+ const gchar *code_verifier,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+ g_autoptr(SoupMessage) msg = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GHashTable) params = NULL;
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+ g_return_if_fail (authorization_code != NULL);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (params, "client_id", priv->client_id);
+ g_hash_table_insert (params, "grant_type", "authorization_code");
+ g_hash_table_insert (params, "code", (gchar *)authorization_code);
+ g_hash_table_insert (params, "redirect_uri", priv->redirect_uri);
+ g_hash_table_insert (params, "code_verifier", (gchar *)code_verifier);
+
+#if WITH_SOUP_2
+ msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params);
+#else
+ msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params));
+#endif
+
+ _rest_proxy_queue_message (REST_PROXY (self),
+#if WITH_SOUP_2
+ g_steal_pointer (&msg),
+#else
+ msg,
+#endif
+ cancellable, rest_oauth2_proxy_fetch_access_token_cb, g_steal_pointer (&task));
+
+}
+
+/**
+ * rest_oauth2_proxy_fetch_access_token_finish:
+ * @self: an #RestOauth2Proxy
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Returns:
+ */
+gboolean
+rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self,
+ GError **error)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+ g_autoptr(SoupMessage) msg = NULL;
+ g_autoptr(GHashTable) params = NULL;
+ g_autoptr(GTask) task = NULL;
+ GBytes *payload;
+
+ task = g_task_new (self, NULL, NULL, NULL);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE);
+
+ if (priv->refresh_token == NULL)
+ {
+ *error = g_error_new (REST_OAUTH2_ERROR,
+ REST_OAUTH2_ERROR_NO_REFRESH_TOKEN,
+ "No refresh token available");
+ return FALSE;
+ }
+
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (params, "client_id", priv->client_id);
+ g_hash_table_insert (params, "refresh_token", priv->refresh_token);
+ g_hash_table_insert (params, "redirect_uri", priv->redirect_uri);
+ g_hash_table_insert (params, "grant_type", "refresh_token");
+
+#if WITH_SOUP_2
+ msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params);
+#else
+ msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params));
+#endif
+ payload = _rest_proxy_send_message (REST_PROXY (self), msg, NULL, error);
+ if (error && *error)
+ {
+ return FALSE;
+ }
+
+ REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, payload, g_steal_pointer (&task));
+ return TRUE;
+}
+
+static void
+rest_oauth2_proxy_refresh_access_token_cb (SoupMessage *msg,
+ GBytes *payload,
+ GError *error,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = user_data;
+ RestOAuth2Proxy *self;
+
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+
+ if (error)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ REST_OAUTH2_PROXY_GET_CLASS (self)->parse_access_token (self, payload, g_steal_pointer (&task));
+}
+
+void
+rest_oauth2_proxy_refresh_access_token_async (RestOAuth2Proxy *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+ g_autoptr(SoupMessage) msg = NULL;
+ g_autoptr(GHashTable) params = NULL;
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (priv->refresh_token == NULL)
+ {
+ g_task_return_new_error (task,
+ REST_OAUTH2_ERROR,
+ REST_OAUTH2_ERROR_NO_REFRESH_TOKEN,
+ "No refresh token available");
+ return;
+ }
+
+ params = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (params, "client_id", priv->client_id);
+ g_hash_table_insert (params, "refresh_token", priv->refresh_token);
+ g_hash_table_insert (params, "redirect_uri", priv->redirect_uri);
+ g_hash_table_insert (params, "grant_type", "refresh_token");
+
+#if WITH_SOUP_2
+ msg = soup_form_request_new_from_hash (SOUP_METHOD_POST, priv->tokenurl, params);
+#else
+ msg = soup_message_new_from_encoded_form (SOUP_METHOD_POST, priv->tokenurl, soup_form_encode_hash (params));
+#endif
+ _rest_proxy_queue_message (REST_PROXY (self),
+#if WITH_SOUP_2
+ g_steal_pointer (&msg),
+#else
+ msg,
+#endif
+ cancellable,
+ rest_oauth2_proxy_refresh_access_token_cb,
+ g_steal_pointer (&task));
+}
+
+/**
+ * rest_oauth2_proxy_refresh_access_token_finish:
+ * @self: an #RestOauth2Proxy
+ * @result: a #GAsyncResult provided to callback
+ * @error: a location for a #GError, or %NULL
+ *
+ * Returns:
+ */
+gboolean
+rest_oauth2_proxy_refresh_access_token_finish (RestOAuth2Proxy *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+const gchar *
+rest_oauth2_proxy_get_auth_url (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->authurl;
+}
+
+void
+rest_oauth2_proxy_set_auth_url (RestOAuth2Proxy *self,
+ const gchar *authurl)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->authurl, authurl) != 0)
+ {
+ g_clear_pointer (&priv->authurl, g_free);
+ priv->authurl = g_strdup (authurl);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_AUTH_URL]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_token_url (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->tokenurl;
+}
+
+void
+rest_oauth2_proxy_set_token_url (RestOAuth2Proxy *self,
+ const gchar *tokenurl)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->tokenurl, tokenurl) != 0)
+ {
+ g_clear_pointer (&priv->tokenurl, g_free);
+ priv->tokenurl = g_strdup (tokenurl);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOKEN_URL]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_redirect_uri (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->redirect_uri;
+}
+
+void
+rest_oauth2_proxy_set_redirect_uri (RestOAuth2Proxy *self,
+ const gchar *redirect_uri)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->redirect_uri, redirect_uri) != 0)
+ {
+ g_clear_pointer (&priv->redirect_uri, g_free);
+ priv->redirect_uri = g_strdup (redirect_uri);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REDIRECT_URI]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_client_id (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->client_id;
+}
+
+void
+rest_oauth2_proxy_set_client_id (RestOAuth2Proxy *self,
+ const gchar *client_id)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->client_id, client_id) != 0)
+ {
+ g_clear_pointer (&priv->client_id, g_free);
+ priv->client_id = g_strdup (client_id);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CLIENT_ID]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_client_secret (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->client_secret;
+}
+
+void
+rest_oauth2_proxy_set_client_secret (RestOAuth2Proxy *self,
+ const gchar *client_secret)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->client_secret, client_secret) != 0)
+ {
+ g_clear_pointer (&priv->client_secret, g_free);
+ priv->client_secret = g_strdup (client_secret);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CLIENT_SECRET]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_access_token (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->access_token;
+}
+
+void
+rest_oauth2_proxy_set_access_token (RestOAuth2Proxy *self,
+ const gchar *access_token)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->access_token, access_token) != 0)
+ {
+ g_clear_pointer (&priv->access_token, g_free);
+ priv->access_token = g_strdup (access_token);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCESS_TOKEN]);
+ }
+}
+
+const gchar *
+rest_oauth2_proxy_get_refresh_token (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->refresh_token;
+}
+
+void
+rest_oauth2_proxy_set_refresh_token (RestOAuth2Proxy *self,
+ const gchar *refresh_token)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ if (g_strcmp0 (priv->refresh_token, refresh_token) != 0)
+ {
+ g_clear_pointer (&priv->refresh_token, g_free);
+ priv->refresh_token = g_strdup (refresh_token);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REFRESH_TOKEN]);
+ }
+}
+
+GDateTime *
+rest_oauth2_proxy_get_expiration_date (RestOAuth2Proxy *self)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+ g_return_val_if_fail (REST_IS_OAUTH2_PROXY (self), NULL);
+
+ return priv->expiration_date;
+}
+
+void
+rest_oauth2_proxy_set_expiration_date (RestOAuth2Proxy *self,
+ GDateTime *expiration_date)
+{
+ RestOAuth2ProxyPrivate *priv = rest_oauth2_proxy_get_instance_private (self);
+
+ g_return_if_fail (REST_IS_OAUTH2_PROXY (self));
+
+ g_clear_pointer (&priv->expiration_date, g_date_time_unref);
+ priv->expiration_date = g_date_time_ref (expiration_date);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPIRATION_DATE]);
+}
diff --git a/rest/rest-oauth2-proxy.h b/rest/rest-oauth2-proxy.h
new file mode 100644
index 0000000..dd4148e
--- /dev/null
+++ b/rest/rest-oauth2-proxy.h
@@ -0,0 +1,101 @@
+/* rest-oauth2-proxy.h
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <rest/rest-proxy.h>
+
+G_BEGIN_DECLS
+
+#define REST_TYPE_OAUTH2_PROXY (rest_oauth2_proxy_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (RestOAuth2Proxy, rest_oauth2_proxy, REST, OAUTH2_PROXY, RestProxy)
+
+struct _RestOAuth2ProxyClass
+{
+ RestProxyClass parent_class;
+
+ void (*parse_access_token) (RestOAuth2Proxy *self,
+ GBytes *payload,
+ GTask *task);
+
+ gpointer padding[8];
+};
+
+enum {
+ REST_OAUTH2_ERROR_NO_REFRESH_TOKEN,
+ REST_OAUTH2_ERROR_ACCESS_TOKEN_EXPIRED,
+};
+
+#define REST_OAUTH2_ERROR rest_oauth2_error_quark ()
+GQuark rest_oauth2_error_quark ();
+
+RestOAuth2Proxy *rest_oauth2_proxy_new (const gchar *authurl,
+ const gchar *tokenurl,
+ const gchar *redirecturl,
+ const gchar *client_id,
+ const gchar *client_secret,
+ const gchar *baseurl);
+gchar *rest_oauth2_proxy_build_authorization_url (RestOAuth2Proxy *self,
+ const gchar *code_challenge,
+ const gchar *scope,
+ gchar **state);
+void rest_oauth2_proxy_fetch_access_token_async (RestOAuth2Proxy *self,
+ const gchar *authorization_code,
+ const gchar *code_verifier,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean rest_oauth2_proxy_fetch_access_token_finish (RestOAuth2Proxy *self,
+ GAsyncResult *result,
+ GError **error);
+gboolean rest_oauth2_proxy_refresh_access_token (RestOAuth2Proxy *self,
+ GError **error);
+void rest_oauth2_proxy_refresh_access_token_async (RestOAuth2Proxy *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean rest_oauth2_proxy_refresh_access_token_finish (RestOAuth2Proxy *self,
+ GAsyncResult *result,
+ GError **error);
+const gchar *rest_oauth2_proxy_get_auth_url (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_auth_url (RestOAuth2Proxy *self,
+ const gchar *tokenurl);
+const gchar *rest_oauth2_proxy_get_token_url (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_token_url (RestOAuth2Proxy *self,
+ const gchar *tokenurl);
+const gchar *rest_oauth2_proxy_get_redirect_uri (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_redirect_uri (RestOAuth2Proxy *self,
+ const gchar *redirect_uri);
+const gchar *rest_oauth2_proxy_get_client_id (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_client_id (RestOAuth2Proxy *self,
+ const gchar *client_id);
+const gchar *rest_oauth2_proxy_get_client_secret (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_client_secret (RestOAuth2Proxy *self,
+ const gchar *client_secret);
+const gchar *rest_oauth2_proxy_get_access_token (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_access_token (RestOAuth2Proxy *self,
+ const gchar *access_token);
+const gchar *rest_oauth2_proxy_get_refresh_token (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_refresh_token (RestOAuth2Proxy *self,
+ const gchar *refresh_token);
+GDateTime *rest_oauth2_proxy_get_expiration_date (RestOAuth2Proxy *self);
+void rest_oauth2_proxy_set_expiration_date (RestOAuth2Proxy *self,
+ GDateTime *expiration_date);
+
+G_END_DECLS
diff --git a/rest/rest-pkce-code-challenge.c b/rest/rest-pkce-code-challenge.c
new file mode 100644
index 0000000..0d936f8
--- /dev/null
+++ b/rest/rest-pkce-code-challenge.c
@@ -0,0 +1,121 @@
+/* rest-pkce-code-challenge.c
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rest-pkce-code-challenge.h"
+#include "rest-utils.h"
+
+G_DEFINE_BOXED_TYPE (RestPkceCodeChallenge, rest_pkce_code_challenge, rest_pkce_code_challenge_copy, rest_pkce_code_challenge_free)
+
+struct _RestPkceCodeChallenge
+{
+ gchar *code_verifier;
+ gchar *code_challenge;
+};
+
+/**
+ * rest_pkce_code_challenge_new_random:
+ *
+ * Creates a new #RestPkceCodeChallenge.
+ *
+ * Returns: (transfer full): A newly created #RestPkceCodeChallenge
+ */
+RestPkceCodeChallenge *
+rest_pkce_code_challenge_new_random (void)
+{
+ RestPkceCodeChallenge *self;
+ gint length = g_random_int_range (43, 128);
+ gsize digest_len = 200;
+ guchar code_verifier_sha256[200];
+ GChecksum *sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+
+ self = g_slice_new0 (RestPkceCodeChallenge);
+ self->code_verifier = random_string (length);
+ g_checksum_update (sha256, (guchar *)self->code_verifier, -1);
+ g_checksum_get_digest (sha256, (guchar *)&code_verifier_sha256, &digest_len);
+
+ self->code_challenge = g_base64_encode (code_verifier_sha256, digest_len);
+ g_strdelimit (self->code_challenge, "=", '\0');
+ g_strdelimit (self->code_challenge, "+", '-');
+ g_strdelimit (self->code_challenge, "/", '_');
+
+ return self;
+}
+
+/**
+ * rest_pkce_code_challenge_copy:
+ * @self: a #RestPkceCodeChallenge
+ *
+ * Makes a deep copy of a #RestPkceCodeChallenge.
+ *
+ * Returns: (transfer full): A newly created #RestPkceCodeChallenge with the same
+ * contents as @self
+ */
+RestPkceCodeChallenge *
+rest_pkce_code_challenge_copy (RestPkceCodeChallenge *self)
+{
+ RestPkceCodeChallenge *copy;
+
+ g_return_val_if_fail (self, NULL);
+
+ copy = g_slice_new0 (RestPkceCodeChallenge);
+ copy->code_verifier = self->code_verifier;
+ copy->code_challenge = self->code_challenge;
+
+ return copy;
+}
+
+/**
+ * rest_pkce_code_challenge_free:
+ * @self: a #RestPkceCodeChallenge
+ *
+ * Frees a #RestPkceCodeChallenge allocated using rest_pkce_code_challenge_new()
+ * or rest_pkce_code_challenge_copy().
+ */
+void
+rest_pkce_code_challenge_free (RestPkceCodeChallenge *self)
+{
+ g_return_if_fail (self);
+
+ g_slice_free (RestPkceCodeChallenge, self);
+}
+
+/**
+ * rest_pkce_code_challenge_get_challenge:
+ *
+ * Returns the Code Challenge for the Pkce verification.
+ *
+ * Returns: the code_challenge
+ */
+const gchar *
+rest_pkce_code_challenge_get_challenge (RestPkceCodeChallenge *self)
+{
+ return self->code_challenge;
+}
+
+/**
+ * rest_pkce_code_challenge_get_verifier:
+ *
+ * Returns the Code Verifier for the Pkce verification.
+ *
+ * Returns: the code_verifier
+ */
+const gchar *
+rest_pkce_code_challenge_get_verifier (RestPkceCodeChallenge *self)
+{
+ return self->code_verifier;
+}
diff --git a/rest/rest-pkce-code-challenge.h b/rest/rest-pkce-code-challenge.h
new file mode 100644
index 0000000..9ce3907
--- /dev/null
+++ b/rest/rest-pkce-code-challenge.h
@@ -0,0 +1,44 @@
+/* rest-pkce-code-challenge.h
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define REST_TYPE_PKCE_CODE_CHALLENGE (rest_pkce_code_challenge_get_type ())
+
+/**
+ * RestPkceCodeChallenge:
+ *
+ * In order to play a Pkce Code Verification during a OAuth2 authorization
+ * you need this structure which handles the algorithmic part.
+ */
+typedef struct _RestPkceCodeChallenge RestPkceCodeChallenge;
+
+GType rest_pkce_code_challenge_get_type (void) G_GNUC_CONST;
+RestPkceCodeChallenge *rest_pkce_code_challenge_new_random (void);
+RestPkceCodeChallenge *rest_pkce_code_challenge_copy (RestPkceCodeChallenge *self);
+void rest_pkce_code_challenge_free (RestPkceCodeChallenge *self);
+const gchar *rest_pkce_code_challenge_get_challenge (RestPkceCodeChallenge *self);
+const gchar *rest_pkce_code_challenge_get_verifier (RestPkceCodeChallenge *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (RestPkceCodeChallenge, rest_pkce_code_challenge_free)
+
+G_END_DECLS
diff --git a/rest/rest-proxy-call.c b/rest/rest-proxy-call.c
index 62b00da..24d952d 100644
--- a/rest/rest-proxy-call.c
+++ b/rest/rest-proxy-call.c
@@ -238,6 +238,8 @@ rest_proxy_call_set_method (RestProxyCall *call,
* @call: The #RestProxyCall
*
* Get the HTTP method to use when making the call, for example GET or POST.
+ *
+ * Returns: (transfer none): the HTTP method
*/
const char *
rest_proxy_call_get_method (RestProxyCall *call)
@@ -1005,19 +1007,23 @@ _call_message_call_completed_cb (SoupMessage *message,
GError *error,
gpointer user_data)
{
- GTask *task = user_data;
+ g_autoptr(GTask) task = user_data;
RestProxyCall *call;
call = REST_PROXY_CALL (g_task_get_source_object (task));
+ if (error)
+ {
+ g_task_return_error (task, error);
+ return;
+ }
+
finish_call (call, message, payload, &error);
if (error != NULL)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
-
- g_object_unref (task);
}
/**
@@ -1158,6 +1164,8 @@ _continuous_call_message_sent_cb (GObject *source,
*
* You may unref the call after calling this function since there is an
* internal reference, or you may unref in the callback.
+ *
+ * Returns: %TRUE on success
*/
gboolean
rest_proxy_call_continuous (RestProxyCall *call,
@@ -1491,11 +1499,7 @@ rest_proxy_call_get_payload_length (RestProxyCall *call)
g_return_val_if_fail (REST_IS_PROXY_CALL (call), 0);
payload = GET_PRIVATE (call)->payload;
-#ifdef WITH_SOUP_2
- return payload ? g_bytes_get_size (payload) - 1 : 0;
-#else
return payload ? g_bytes_get_size (payload) : 0;
-#endif
}
/**
diff --git a/rest/rest-proxy.c b/rest/rest-proxy.c
index 171f6cb..8231b6f 100644
--- a/rest/rest-proxy.c
+++ b/rest/rest-proxy.c
@@ -763,7 +763,7 @@ message_finished_cb (SoupSession *session,
GError *error = NULL;
body = g_bytes_new (message->response_body->data,
- message->response_body->length + 1);
+ message->response_body->length);
data->callback (message, body, error, data->user_data);
g_free (data);
}
@@ -894,7 +894,7 @@ _rest_proxy_send_message (RestProxy *proxy,
#ifdef WITH_SOUP_2
soup_session_send_message (priv->session, message);
body = g_bytes_new (message->response_body->data,
- message->response_body->length + 1);
+ message->response_body->length);
#else
body = soup_session_send_and_read (priv->session,
message,
diff --git a/rest/rest-utils.c b/rest/rest-utils.c
new file mode 100644
index 0000000..939d49e
--- /dev/null
+++ b/rest/rest-utils.c
@@ -0,0 +1,44 @@
+/* rest-utils.c
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rest-utils.h"
+
+/**
+ * random_string:
+ * @length: the length of the random string
+ *
+ * Creates a random string from a given alphabeth with length @length
+ *
+ * Returns: (transfer full): a random string
+ */
+gchar *
+random_string (guint length)
+{
+ g_autoptr(GRand) rand = g_rand_new ();
+ gchar *buffer = g_slice_alloc0 (sizeof (gchar) * length + 1);
+ gchar alphabeth[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
+
+ for (guint i = 0; i < length; i++)
+ {
+ buffer[i] = alphabeth[g_rand_int (rand) % (sizeof (alphabeth) - 1)];
+ }
+ buffer[length] = '\0';
+
+ return buffer;
+}
+
diff --git a/rest/rest-utils.h b/rest/rest-utils.h
new file mode 100644
index 0000000..cd61145
--- /dev/null
+++ b/rest/rest-utils.h
@@ -0,0 +1,27 @@
+/* rest-utils.h
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gchar *random_string (guint length);
+
+G_END_DECLS
diff --git a/rest/rest.h b/rest/rest.h
new file mode 100644
index 0000000..9bc964c
--- /dev/null
+++ b/rest/rest.h
@@ -0,0 +1,35 @@
+/* rest.h
+ *
+ * Copyright 2021 Günther Wagner <info@gunibert.de>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define REST_INSIDE
+# include "rest-proxy.h"
+# include "rest-proxy-call.h"
+# include "rest-oauth2-proxy.h"
+# include "rest-utils.h"
+# include "rest-pkce-code-challenge.h"
+#undef REST_INSIDE
+
+G_END_DECLS