4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 * SECTION: e-soup-auth-bearer
20 * @include: libedataserver/libedataserver.h
21 * @short_description: OAuth 2.0 support for libsoup
23 * #ESoupAuthBearer adds libsoup support for the use of bearer tokens in
24 * HTTP requests to access OAuth 2.0 protected resources, as defined in
25 * <ulink url="http://tools.ietf.org/html/rfc6750">RFC 6750</ulink>.
27 * An #EBackend should integrate #ESoupAuthBearer first by adding it as a
28 * feature to a #SoupSession's #SoupAuthManager, then from a #SoupSession
29 * #SoupSession::authenticate handler call e_source_get_oauth2_access_token()
30 * and pass the results to e_soup_auth_bearer_set_access_token().
33 #include "evolution-data-server-config.h"
35 #include "e-soup-auth-bearer.h"
39 #define E_SOUP_AUTH_BEARER_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_SOUP_AUTH_BEARER, ESoupAuthBearerPrivate))
43 #define AUTH_STRENGTH 1
45 #define EXPIRY_INVALID ((time_t) -1)
47 struct _ESoupAuthBearerPrivate
{
58 e_soup_auth_bearer_finalize (GObject
*object
)
60 ESoupAuthBearerPrivate
*priv
;
62 priv
= E_SOUP_AUTH_BEARER_GET_PRIVATE (object
);
64 g_free (priv
->access_token
);
66 /* Chain up to parent's finalize() method. */
67 G_OBJECT_CLASS (e_soup_auth_bearer_parent_class
)->finalize (object
);
71 e_soup_auth_bearer_update (SoupAuth
*auth
,
73 GHashTable
*auth_header
)
75 if (message
&& message
->status_code
== SOUP_STATUS_UNAUTHORIZED
) {
76 ESoupAuthBearer
*bearer
;
78 g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (auth
), FALSE
);
80 bearer
= E_SOUP_AUTH_BEARER (auth
);
82 /* Expire the token, it's likely to be invalid. */
83 bearer
->priv
->expiry
= EXPIRY_INVALID
;
92 e_soup_auth_bearer_get_protection_space (SoupAuth
*auth
,
95 /* XXX Not sure what to do here. Need to return something. */
97 return g_slist_prepend (NULL
, g_strdup (""));
101 e_soup_auth_bearer_is_authenticated (SoupAuth
*auth
)
103 ESoupAuthBearer
*bearer
;
104 gboolean authenticated
= FALSE
;
106 bearer
= E_SOUP_AUTH_BEARER (auth
);
108 if (!e_soup_auth_bearer_is_expired (bearer
))
109 authenticated
= (bearer
->priv
->access_token
!= NULL
);
111 return authenticated
;
115 e_soup_auth_bearer_get_authorization (SoupAuth
*auth
,
116 SoupMessage
*message
)
118 ESoupAuthBearer
*bearer
;
120 bearer
= E_SOUP_AUTH_BEARER (auth
);
122 return g_strdup_printf ("Bearer %s", bearer
->priv
->access_token
);
126 e_soup_auth_bearer_class_init (ESoupAuthBearerClass
*class)
128 GObjectClass
*object_class
;
129 SoupAuthClass
*auth_class
;
131 g_type_class_add_private (class, sizeof (ESoupAuthBearerPrivate
));
133 /* Keep the "e" prefix on private methods
134 * so we don't step on libsoup's namespace. */
136 object_class
= G_OBJECT_CLASS (class);
137 object_class
->finalize
= e_soup_auth_bearer_finalize
;
139 auth_class
= SOUP_AUTH_CLASS (class);
140 auth_class
->scheme_name
= "Bearer";
141 auth_class
->strength
= AUTH_STRENGTH
;
142 auth_class
->update
= e_soup_auth_bearer_update
;
143 auth_class
->get_protection_space
= e_soup_auth_bearer_get_protection_space
;
144 auth_class
->is_authenticated
= e_soup_auth_bearer_is_authenticated
;
145 auth_class
->get_authorization
= e_soup_auth_bearer_get_authorization
;
149 e_soup_auth_bearer_init (ESoupAuthBearer
*bearer
)
151 bearer
->priv
= E_SOUP_AUTH_BEARER_GET_PRIVATE (bearer
);
152 bearer
->priv
->expiry
= EXPIRY_INVALID
;
156 * e_soup_auth_bearer_set_access_token:
157 * @bearer: an #ESoupAuthBearer
158 * @access_token: an OAuth 2.0 access token
159 * @expires_in_seconds: expiry for @access_token, or 0 if unknown
161 * This function is analogous to soup_auth_authenticate() for "Basic" HTTP
162 * authentication, except it takes an OAuth 2.0 access token instead of a
163 * username and password.
165 * If @expires_in_seconds is greater than zero, soup_auth_is_authenticated()
166 * will return %FALSE after the given number of seconds have elapsed.
171 e_soup_auth_bearer_set_access_token (ESoupAuthBearer
*bearer
,
172 const gchar
*access_token
,
173 gint expires_in_seconds
)
175 gboolean was_authenticated
;
176 gboolean now_authenticated
;
178 g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer
));
180 was_authenticated
= soup_auth_is_authenticated (SOUP_AUTH (bearer
));
182 g_free (bearer
->priv
->access_token
);
183 bearer
->priv
->access_token
= g_strdup (access_token
);
185 if (expires_in_seconds
> 0)
186 bearer
->priv
->expiry
= time (NULL
) + expires_in_seconds
- 1;
188 bearer
->priv
->expiry
= EXPIRY_INVALID
;
190 now_authenticated
= soup_auth_is_authenticated (SOUP_AUTH (bearer
));
192 if (was_authenticated
!= now_authenticated
)
195 SOUP_AUTH_IS_AUTHENTICATED
);
199 * e_soup_auth_bearer_is_expired:
200 * @bearer: an #ESoupAuthBearer
202 * Returns: Whether the set token is expired. It is considered expired even
203 * if the e_soup_auth_bearer_set_access_token() was called set yet.
208 e_soup_auth_bearer_is_expired (ESoupAuthBearer
*bearer
)
210 gboolean expired
= TRUE
;
212 g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer
), TRUE
);
214 if (bearer
->priv
->expiry
!= EXPIRY_INVALID
)
215 expired
= (bearer
->priv
->expiry
<= time (NULL
));