Skip to content

Commit f98900e

Browse files
nanangizzclaude
andcommitted
Add pjsua2 onAuthChallenge with sync fallback flag
Change async auth callback return type from void to pj_bool_t. PJ_TRUE means app handles the challenge (async path), PJ_FALSE lets the library fall through to synchronous reinit_req + send. This allows pjsua2 to always register the callback while the default no-op safely falls back to sync auth. Add AuthChallenge class with respond()/abandon() methods and OnAuthChallengeParam for the pjsua2 Account::onAuthChallenge() virtual callback. Thread tdata and handled flag through pjsua bridge layer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 128656a commit f98900e

File tree

9 files changed

+164
-15
lines changed

9 files changed

+164
-15
lines changed

pjsip/include/pjsip/sip_auth.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,8 +674,14 @@ typedef struct pjsip_auth_clt_async_on_chal_param
674674
* to be passed to #pjsip_auth_clt_async_send_req().
675675
* @param param The callback parameter containing the original request
676676
* and the response with the challenge.
677+
*
678+
* @return PJ_TRUE if the application handles the challenge
679+
* (will call pjsip_auth_clt_async_send_req() or
680+
* pjsip_auth_clt_async_abandon() later).
681+
* PJ_FALSE to let the library handle authentication
682+
* via the synchronous path.
677683
*/
678-
typedef void pjsip_auth_clt_async_on_challenge(
684+
typedef pj_bool_t pjsip_auth_clt_async_on_challenge(
679685
pjsip_auth_clt_sess *sess,
680686
void *token,
681687
const pjsip_auth_clt_async_on_chal_param* param);

pjsip/include/pjsua-lib/pjsua.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,21 @@ typedef struct pjsua_on_auth_challenge_param
12811281
*/
12821282
const pjsip_rx_data *rdata;
12831283

1284+
/**
1285+
* The original request that was challenged. Needed to build the
1286+
* authenticated retry. Only valid during the callback.
1287+
*/
1288+
pjsip_tx_data *tdata;
1289+
1290+
/**
1291+
* Output: set to PJ_TRUE if the application handles the challenge
1292+
* (will call pjsip_auth_clt_async_send_req() or
1293+
* pjsip_auth_clt_async_abandon() later).
1294+
* Default PJ_FALSE means the library handles authentication
1295+
* via the synchronous path.
1296+
*/
1297+
pj_bool_t handled;
1298+
12841299
} pjsua_on_auth_challenge_param;
12851300

12861301

@@ -2261,7 +2276,7 @@ typedef struct pjsua_callback
22612276
* If this callback is not set, the library will handle authentication
22622277
* automatically using the configured credentials (synchronous path).
22632278
*/
2264-
void (*on_auth_challenge)(const pjsua_on_auth_challenge_param *param);
2279+
void (*on_auth_challenge)(pjsua_on_auth_challenge_param *param);
22652280

22662281
} pjsua_callback;
22672282

pjsip/include/pjsua-lib/pjsua_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,8 @@ void pjsua_acc_end_ip_change(pjsua_acc *acc);
10731073
/*
10741074
* Bridge callback: maps low-level auth challenge to pjsua on_auth_challenge.
10751075
*/
1076-
void pjsua_auth_on_challenge(pjsip_auth_clt_sess *sess,
1076+
pj_bool_t pjsua_auth_on_challenge(
1077+
pjsip_auth_clt_sess *sess,
10771078
void *token,
10781079
const pjsip_auth_clt_async_on_chal_param *param);
10791080

pjsip/include/pjsua2/account.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,58 @@ struct OnSendRequestParam
18811881
};
18821882

18831883

1884+
/**
1885+
* Represents a pending authentication challenge.
1886+
* Call respond() to resend with authentication, or abandon() to give up.
1887+
* If neither is called before the callback returns, the library handles
1888+
* authentication automatically using configured credentials (sync path).
1889+
* This object is only valid during the onAuthChallenge() callback.
1890+
*/
1891+
class AuthChallenge
1892+
{
1893+
public:
1894+
/**
1895+
* Respond to the authentication challenge by building and sending
1896+
* an authenticated request. Uses credentials currently configured
1897+
* on the account.
1898+
*
1899+
* @return PJ_SUCCESS on success.
1900+
*/
1901+
pj_status_t respond();
1902+
1903+
/**
1904+
* Abandon the authentication challenge. The pending request will
1905+
* not be resent.
1906+
*
1907+
* @return PJ_SUCCESS on success.
1908+
*/
1909+
pj_status_t abandon();
1910+
1911+
private:
1912+
friend class Endpoint;
1913+
1914+
pjsua_on_auth_challenge_param *param;
1915+
};
1916+
1917+
/**
1918+
* Parameters for Account::onAuthChallenge() callback.
1919+
*/
1920+
struct OnAuthChallengeParam
1921+
{
1922+
/** Account ID associated with the challenged request. */
1923+
pjsua_acc_id accId;
1924+
1925+
/** Call ID, or PJSUA_INVALID_ID for non-call requests. */
1926+
pjsua_call_id callId;
1927+
1928+
/** The 401/407 response containing the challenge. */
1929+
SipRxData rdata;
1930+
1931+
/** The authentication challenge. Call respond() or abandon(). */
1932+
AuthChallenge challenge;
1933+
};
1934+
1935+
18841936
/**
18851937
* Parameters for presNotify() account method.
18861938
*/
@@ -2371,6 +2423,17 @@ class Account
23712423
virtual void onMwiInfo(OnMwiInfoParam &prm)
23722424
{ PJ_UNUSED_ARG(prm); }
23732425

2426+
/**
2427+
* Called when a 401/407 challenge is received. Override to handle
2428+
* authentication asynchronously. Call prm.challenge.respond() to
2429+
* resend with authentication, or prm.challenge.abandon() to give up.
2430+
* If neither is called, the library handles it automatically.
2431+
*
2432+
* @param prm Callback parameter.
2433+
*/
2434+
virtual void onAuthChallenge(OnAuthChallengeParam &prm)
2435+
{ PJ_UNUSED_ARG(prm); }
2436+
23742437
private:
23752438
friend class Endpoint;
23762439
friend class Buddy;

pjsip/include/pjsua2/endpoint.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,8 @@ class Endpoint
23292329
pjsua_acc_id acc_id);
23302330
static void on_mwi_info(pjsua_acc_id acc_id,
23312331
pjsua_mwi_info *mwi_info);
2332+
static void on_auth_challenge(
2333+
pjsua_on_auth_challenge_param *param);
23322334
static void on_acc_find_for_incoming(const pjsip_rx_data *rdata,
23332335
pjsua_acc_id* acc_id);
23342336
static void on_buddy_state(pjsua_buddy_id buddy_id);

pjsip/src/pjsip/sip_auth_client.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,13 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_async_impl_on_challenge(
19651965

19661966
cb_param = *param;
19671967
cb_param.user_data = sess->async_opt->user_data;
1968-
(*sess->async_opt->cb)(sess, token, &cb_param);
1969-
return PJ_SUCCESS;
1968+
if ((*sess->async_opt->cb)(sess, token, &cb_param)) {
1969+
return PJ_SUCCESS;
1970+
}
1971+
1972+
/* Callback chose not to handle — invalidate token, let caller
1973+
* fall through to synchronous path.
1974+
*/
1975+
pj_bzero(token->signature, 4);
1976+
return PJ_EINVALIDOP;
19701977
}

pjsip/src/pjsua-lib/pjsua_acc.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ static void schedule_reregistration(pjsua_acc *acc);
3636
static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te);
3737

3838
/* Bridge: maps low-level auth challenge -> pjsua on_auth_challenge */
39-
void pjsua_auth_on_challenge(pjsip_auth_clt_sess *sess,
39+
pj_bool_t pjsua_auth_on_challenge(
40+
pjsip_auth_clt_sess *sess,
4041
void *token,
4142
const pjsip_auth_clt_async_on_chal_param *param)
4243
{
@@ -48,7 +49,8 @@ void pjsua_auth_on_challenge(pjsip_auth_clt_sess *sess,
4849

4950
/* Determine call_id from rdata -> dialog -> mod_data */
5051
if (param->rdata) {
51-
pjsip_dialog *dlg = pjsip_rdata_get_dlg(param->rdata);
52+
pjsip_dialog *dlg = pjsip_rdata_get_dlg(
53+
(pjsip_rx_data*)param->rdata);
5254
if (dlg) {
5355
pjsua_call *call =
5456
(pjsua_call*)dlg->mod_data[pjsua_var.mod.id];
@@ -63,8 +65,11 @@ void pjsua_auth_on_challenge(pjsip_auth_clt_sess *sess,
6365
cb_param.auth_sess = sess;
6466
cb_param.token = token;
6567
cb_param.rdata = param->rdata;
68+
cb_param.tdata = param->tdata;
6669

6770
(*pjsua_var.ua_cfg.cb.on_auth_challenge)(&cb_param);
71+
72+
return cb_param.handled;
6873
}
6974

7075
/*

pjsip/src/pjsua2/endpoint.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,51 @@ void Endpoint::on_mwi_info(pjsua_acc_id acc_id,
11641164
acc->onMwiInfo(prm);
11651165
}
11661166

1167+
pj_status_t AuthChallenge::respond()
1168+
{
1169+
pjsip_tx_data *new_tdata;
1170+
pj_status_t status;
1171+
1172+
status = pjsip_auth_clt_reinit_req(param->auth_sess,
1173+
(pjsip_rx_data*)param->rdata,
1174+
param->tdata, &new_tdata);
1175+
if (status == PJ_SUCCESS) {
1176+
status = pjsip_auth_clt_async_send_req(param->auth_sess,
1177+
param->token, new_tdata);
1178+
}
1179+
param->handled = PJ_TRUE;
1180+
return status;
1181+
}
1182+
1183+
pj_status_t AuthChallenge::abandon()
1184+
{
1185+
pj_status_t status = pjsip_auth_clt_async_abandon(param->auth_sess,
1186+
param->token);
1187+
param->handled = PJ_TRUE;
1188+
return status;
1189+
}
1190+
1191+
void Endpoint::on_auth_challenge(pjsua_on_auth_challenge_param *param)
1192+
{
1193+
Account *acc = lookupAcc(param->acc_id, "on_auth_challenge()");
1194+
if (!acc) {
1195+
/* No account — leave handled=false, sync fallback */
1196+
return;
1197+
}
1198+
1199+
OnAuthChallengeParam prm;
1200+
prm.accId = param->acc_id;
1201+
prm.callId = param->call_id;
1202+
if (param->rdata)
1203+
prm.rdata.fromPj(*(pjsip_rx_data*)param->rdata);
1204+
prm.challenge.param = param;
1205+
1206+
acc->onAuthChallenge(prm);
1207+
/* If app called respond() or abandon(), param->handled is PJ_TRUE.
1208+
* If not (default no-op), param->handled stays PJ_FALSE → sync fallback.
1209+
*/
1210+
}
1211+
11671212
void Endpoint::on_acc_find_for_incoming(const pjsip_rx_data *rdata,
11681213
pjsua_acc_id* acc_id)
11691214
{
@@ -2112,6 +2157,7 @@ void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error)
21122157
ua_cfg.cb.on_rejected_incoming_call = &Endpoint::on_rejected_incoming_call;
21132158
ua_cfg.cb.on_conf_op_completed = &Endpoint::on_conf_op_completed;
21142159
ua_cfg.cb.on_vid_conf_op_completed = &Endpoint::on_vid_conf_op_completed;
2160+
ua_cfg.cb.on_auth_challenge = &Endpoint::on_auth_challenge;
21152161

21162162
/* Init! */
21172163
PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) );

pjsip/src/test/auth_async_test.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ static pj_status_t dummy_send_impl(pjsip_auth_clt_sess *sess,
169169
*****************************************************************************/
170170

171171
/* Async challenge callback - used by the sync and deferred send tests. */
172-
static void on_auth_challenge(pjsip_auth_clt_sess *sess,
173-
void *token,
174-
const pjsip_auth_clt_async_on_chal_param *param)
172+
static pj_bool_t on_auth_challenge(pjsip_auth_clt_sess *sess,
173+
void *token,
174+
const pjsip_auth_clt_async_on_chal_param *param)
175175
{
176176
test_ctx_t *ctx = (test_ctx_t *)param->user_data;
177177
pjsip_tx_data *new_tdata = NULL;
@@ -189,7 +189,7 @@ static void on_auth_challenge(pjsip_auth_clt_sess *sess,
189189
ctx->reinit_status = status;
190190

191191
if (status != PJ_SUCCESS || !new_tdata)
192-
return;
192+
return PJ_TRUE;
193193

194194
if (ctx->sync) {
195195
/* Synchronous path: send from within the callback.
@@ -207,6 +207,7 @@ static void on_auth_challenge(pjsip_auth_clt_sess *sess,
207207
* async_auth_send_impl -> pjsip_regc_send.
208208
*/
209209
}
210+
return PJ_TRUE;
210211
}
211212

212213
/* Registration completion callback. */
@@ -411,7 +412,7 @@ static struct {
411412
void *token;
412413
} g_double_send;
413414

414-
static void on_challenge_for_double_send(
415+
static pj_bool_t on_challenge_for_double_send(
415416
pjsip_auth_clt_sess *sess,
416417
void *token,
417418
const pjsip_auth_clt_async_on_chal_param *param)
@@ -422,7 +423,7 @@ static void on_challenge_for_double_send(
422423
status = pjsip_auth_clt_reinit_req(sess, param->rdata, param->tdata,
423424
&new_tdata);
424425
if (status != PJ_SUCCESS || !new_tdata)
425-
return;
426+
return PJ_TRUE;
426427

427428
/* Remember the session and token before consuming them */
428429
g_double_send.sess = sess;
@@ -431,6 +432,7 @@ static void on_challenge_for_double_send(
431432
/* First (and only legitimate) send - this clears token->signature */
432433
pjsip_auth_clt_async_send_req(sess, token, new_tdata);
433434
g_double_send.fired = PJ_TRUE;
435+
return PJ_TRUE;
434436
}
435437

436438
static int double_send_test(const pj_str_t *registrar_uri)
@@ -542,7 +544,7 @@ static struct {
542544
pj_bool_t fired;
543545
} g_abandoned;
544546

545-
static void on_challenge_no_send(
547+
static pj_bool_t on_challenge_no_send(
546548
pjsip_auth_clt_sess *sess,
547549
void *token,
548550
const pjsip_auth_clt_async_on_chal_param *param)
@@ -555,6 +557,7 @@ static void on_challenge_no_send(
555557
* pjsip_auth_clt_async_send_req.
556558
*/
557559
g_abandoned.fired = PJ_TRUE;
560+
return PJ_TRUE;
558561
}
559562

560563
static int abandoned_token_test(const pj_str_t *registrar_uri)
@@ -636,7 +639,7 @@ static struct {
636639
void *token;
637640
} g_abandon_api;
638641

639-
static void on_challenge_do_abandon(
642+
static pj_bool_t on_challenge_do_abandon(
640643
pjsip_auth_clt_sess *sess,
641644
void *token,
642645
const pjsip_auth_clt_async_on_chal_param *param)
@@ -650,6 +653,7 @@ static void on_challenge_do_abandon(
650653

651654
/* Explicitly abandon: regc's abandon_impl will call on_reg_complete */
652655
pjsip_auth_clt_async_abandon(sess, token);
656+
return PJ_TRUE;
653657
}
654658

655659
static int abandon_api_test(const pj_str_t *registrar_uri)

0 commit comments

Comments
 (0)