/*
* e-soup-auth-bearer.c
*
* This library 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.
*
* This library 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 library. If not, see .
*
*/
/**
* SECTION: e-soup-auth-bearer
* @include: libedataserver/libedataserver.h
* @short_description: OAuth 2.0 support for libsoup
*
* #ESoupAuthBearer adds libsoup support for the use of bearer tokens in
* HTTP requests to access OAuth 2.0 protected resources, as defined in
* RFC 6750.
*
* An #EBackend should integrate #ESoupAuthBearer first by adding it as a
* feature to a #SoupSession's #SoupAuthManager, then from a #SoupSession
* #SoupSession::authenticate handler call e_source_get_oauth2_access_token()
* and pass the results to e_soup_auth_bearer_set_access_token().
**/
#ifdef HAVE_CONFIG_H
#include
#endif
#include "e-soup-auth-bearer.h"
#include
#define E_SOUP_AUTH_BEARER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerPrivate))
#define AUTH_STRENGTH 1
#define EXPIRY_INVALID ((time_t) -1)
struct _ESoupAuthBearerPrivate {
gchar *access_token;
time_t expiry;
};
G_DEFINE_TYPE (
ESoupAuthBearer,
e_soup_auth_bearer,
SOUP_TYPE_AUTH)
static gboolean
e_soup_auth_bearer_is_expired (ESoupAuthBearer *bearer)
{
gboolean expired = FALSE;
if (bearer->priv->expiry != EXPIRY_INVALID)
expired = (bearer->priv->expiry < time (NULL));
return expired;
}
static void
e_soup_auth_bearer_finalize (GObject *object)
{
ESoupAuthBearerPrivate *priv;
priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (object);
g_free (priv->access_token);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_soup_auth_bearer_parent_class)->finalize (object);
}
static gboolean
e_soup_auth_bearer_update (SoupAuth *auth,
SoupMessage *message,
GHashTable *auth_header)
{
/* XXX Not sure what to do here. Discard the access token? */
return TRUE;
}
static GSList *
e_soup_auth_bearer_get_protection_space (SoupAuth *auth,
SoupURI *source_uri)
{
/* XXX Not sure what to do here. Need to return something. */
return g_slist_prepend (NULL, g_strdup (""));
}
static gboolean
e_soup_auth_bearer_is_authenticated (SoupAuth *auth)
{
ESoupAuthBearer *bearer;
gboolean authenticated = FALSE;
bearer = E_SOUP_AUTH_BEARER (auth);
if (!e_soup_auth_bearer_is_expired (bearer))
authenticated = (bearer->priv->access_token != NULL);
return authenticated;
}
static gchar *
e_soup_auth_bearer_get_authorization (SoupAuth *auth,
SoupMessage *message)
{
ESoupAuthBearer *bearer;
bearer = E_SOUP_AUTH_BEARER (auth);
return g_strdup_printf ("Bearer %s", bearer->priv->access_token);
}
static void
e_soup_auth_bearer_class_init (ESoupAuthBearerClass *class)
{
GObjectClass *object_class;
SoupAuthClass *auth_class;
g_type_class_add_private (class, sizeof (ESoupAuthBearerPrivate));
/* Keep the "e" prefix on private methods
* so we don't step on libsoup's namespace. */
object_class = G_OBJECT_CLASS (class);
object_class->finalize = e_soup_auth_bearer_finalize;
auth_class = SOUP_AUTH_CLASS (class);
auth_class->scheme_name = "Bearer";
auth_class->strength = AUTH_STRENGTH;
auth_class->update = e_soup_auth_bearer_update;
auth_class->get_protection_space = e_soup_auth_bearer_get_protection_space;
auth_class->is_authenticated = e_soup_auth_bearer_is_authenticated;
auth_class->get_authorization = e_soup_auth_bearer_get_authorization;
}
static void
e_soup_auth_bearer_init (ESoupAuthBearer *bearer)
{
bearer->priv = E_SOUP_AUTH_BEARER_GET_PRIVATE (bearer);
bearer->priv->expiry = EXPIRY_INVALID;
}
/**
* e_soup_auth_bearer_set_access_token:
* @bearer: an #ESoupAuthBearer
* @access_token: an OAuth 2.0 access token
* @expires_in_seconds: expiry for @access_token, or 0 if unknown
*
* This function is analogous to soup_auth_authenticate() for "Basic" HTTP
* authentication, except it takes an OAuth 2.0 access token instead of a
* username and password.
*
* If @expires_in_seconds is greater than zero, soup_auth_is_authenticated()
* will return %FALSE after the given number of seconds have elapsed.
*
* Since: 3.10
**/
void
e_soup_auth_bearer_set_access_token (ESoupAuthBearer *bearer,
const gchar *access_token,
gint expires_in_seconds)
{
gboolean was_authenticated;
gboolean now_authenticated;
g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer));
was_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer));
g_free (bearer->priv->access_token);
bearer->priv->access_token = g_strdup (access_token);
if (expires_in_seconds > 0)
bearer->priv->expiry = time (NULL) + expires_in_seconds;
else
bearer->priv->expiry = EXPIRY_INVALID;
now_authenticated = soup_auth_is_authenticated (SOUP_AUTH (bearer));
if (was_authenticated != now_authenticated)
g_object_notify (
G_OBJECT (bearer),
SOUP_AUTH_IS_AUTHENTICATED);
}