Change libsoup authenication within SoupSession

275 views Asked by At

I have a working libsoup client which sends data with a HTTP POST and Basic Authentication. The authentication is handled within libsoup through a callback - when the server requires authentication libsoup signals it with the callback - and then the function soup_auth_authenticate() gets passed a given object of type SoupAuth together with the username and password.

#include <iostream>
#include <iomanip>
#include <string>
#include <libsoup/soup.h>
using namespace std;

void authenticate(SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean retrying, gpointer data) {
    soup_auth_authenticate(auth, "foo", "bar");
}

int main() {
    SoupSession* session = soup_session_new_with_options(SOUP_SESSION_USER_AGENT, "stackoverflow",
                                                         SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
                                                         NULL);
    g_signal_connect(session, "authenticate", G_CALLBACK(authenticate), nullptr);
    SoupMessage* post_msg = soup_message_new("POST", "https://example.org/work.php");
    string formdata = "first_name=Captain&last_name=Picard";
    soup_message_set_request(post_msg, "application/x-www-form-urlencoded", SOUP_MEMORY_COPY, formdata.c_str(), formdata.size());
    soup_session_send_message(session, post_msg);
    cout << left << setw(22) << "status code: " << right << post_msg->status_code << "\n";
    cout << left << setw(22) << "reason phrase: " << right << post_msg->reason_phrase << "\n";
    cout << left << setw(22) << "response body length: " << right << post_msg->response_body->length << "\n";
    cout << left << setw(22) << "response body data: " << right << post_msg->response_body->data << "\n";
    // TODO call soup_session_send_message() again with modified username and password
    return EXIT_SUCCESS;
}

You can compile this with g++ -o sample sample.cpp -Wall -pedantic -g `pkg-config libsoup-2.4 --cflags --libs`. When you need to test this, please change the domain from example.org to flapflap.eu which will give you a working endpoint.

What should I do when I want to send a different username or password in a subsequent call? The library will not use the callback anymore because the authentication is set up and working already.

Do I need create a new SoupSession? Or can I access the current SoupAuth and call soup_auth_authenicate() directly? I want to keep the client working fast.

Thank you for your help

1

There are 1 answers

2
ryyker On

The background information at bottom indicates that you do not need to create a new SoupSession to make subsequent authentication requests. It is not clear though that the soup_auth_authenticate() call is the method to do that. Following is the list of authentication related calls from this libsoup page:

SoupAuth *  soup_auth_new ()
gboolean    soup_auth_update ()
gboolean    soup_auth_negotiate_supported ()
gboolean    soup_auth_is_for_proxy ()
const char *    soup_auth_get_scheme_name ()
const char *    soup_auth_get_host ()
const char *    soup_auth_get_realm ()
char *  soup_auth_get_info ()
void    soup_auth_authenticate ()
gboolean    soup_auth_can_authenticate ()
gboolean    soup_auth_is_authenticated ()
gboolean    soup_auth_is_ready ()
char *  soup_auth_get_authorization ()
GSList *    soup_auth_get_protection_space ()
void    soup_auth_free_protection_space ()

Reading between the lines in this Basics page seems to suggest it is possible to make multiple authentication requests in a single SoupSession.

Handling Authentication

SoupSession handles most of the details of HTTP authentication for you. If it receives a 401 ("Unauthorized") or 407 ("Proxy Authentication Required") response, the session will emit the authenticate signal, providing you with a SoupAuth object indicating the authentication type ("Basic", "Digest", or "NTLM") and the realm name provided by the server. If you have a username and password available (or can generate one), call soup_auth_authenticate to give the information to libsoup. The session will automatically requeue the message and try it again with that authentication information. (If you don't call soup_auth_authenticate, the session will just return the message to the application with its 401 or 407 status.)

If the server doesn't accept the username and password provided, the session will emit authenticate again, with the retrying parameter set to TRUE. This lets the application know that the information it provided earlier was incorrect, and gives it a chance to try again. If this username/password pair also doesn't work, the session will contine to emit authenticate again and again until the provided username/password successfully authenticates, or until the signal handler fails to call soup_auth_authenticate, at which point libsoup will allow the message to fail (with status 401 or 407).

If you need to handle authentication asynchronously (eg, to pop up a password dialog without recursively entering the main loop), you can do that as well. Just call soup_session_pause_message on the message before returning from the signal handler, and g_object_ref the SoupAuth. Then, later on, after calling soup_auth_authenticate (or deciding not to), call soup_session_unpause_message to resume the paused message.

This Manpagez post also discusses more than one call to authenticate per session:

Most applications will only need a single SoupSession; the primary reason you might need multiple sessions is if you need to have multiple independent authentication contexts. (Eg, you are connecting to a server and authenticating as two different users at different times; the easiest way to ensure that each SoupMessage is sent with the authentication information you intended is to use one session for the first user, and a second session for the other user.)