1

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

Peter
  • 2,240
  • 3
  • 23
  • 37
  • You are showing a sequential, single thread architecture. Once execution flow exits the closing `}` of the `main(){...}` function, all control to the process(es) created, if they even still exist are not accessible to this process, as it no longer exists. I would think that you would need to include an [event driven](https://stackoverflow.com/questions/11062674/event-driven-model-in-c-with-sockets), threaded architecture that would allow you to stay in perpetual execution until a kill signal is invoked, accept user inputs for such things as logging in, passing credentials, logging out, etc. – ryyker Jun 04 '20 at 12:59
  • Likley a misunderstanding. I want to provide a simple example, threading doesn't matter here for me. Imagine the next call where the TODO is placed but now with different username/password. – Peter Jun 04 '20 at 13:05
  • Clearly, you do not have to create a new session to make subsequent authentication requests, however, if a first attempt fails, your code should be set up to try again, perhaps in a loop. See 2nd paragraph under _Handling Authentication_ in answer. – ryyker Jun 04 '20 at 13:51
  • When the authentication fails libsoup will keep using the provided callback and set the parameter 'retrying' on true. It does that here actually 20 times till it gives up. In my case the first and probably many other messages were already send successful and I just want change my username/password deliberately. – Peter Jun 04 '20 at 14:01
  • Hmm, _changing_ username and password within the context of an existing session as opposed to sending a request for an _additional_ user authentication sounds like it would require using some different calls. – ryyker Jun 04 '20 at 14:11

1 Answers1

1

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.)

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • 1
    Yep. My reading of the tutorial is the same. It sounds like it is possible to change the authentication in a SoupSession for subsequent calls, also when the initial credentials are correct. But it looks like your last paragraph is the sane option, creating another SoupSession for new authentication context. – Peter Jun 04 '20 at 13:52
  • 1
    @Peter - I am not sure that the last paragraph _discourages_ multiple authentications in a single session. It would depend on what the application needs are. You might try it both ways to see experientially what advantages/pitfalls each method offers. – ryyker Jun 04 '20 at 14:04