@@ -74,11 +74,12 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
7474 /// <param name="username">username to trigger challenges for</param>
7575 /// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
7676 /// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
77+ /// <param name="customParameters">optional custom parameters to include</param>
7778 /// <param name="cancellationToken">optional</param>
7879 /// <returns>PIResponse object or null on error</returns>
79- public async Task < PIResponse ? > TriggerChallenges ( string username , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , CancellationToken cancellationToken = default )
80+ public async Task < PIResponse ? > TriggerChallenges ( string username , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
8081 {
81- if ( await GetAuthToken ( cancellationToken ) is false )
82+ if ( await GetAuthToken ( customParameters , cancellationToken ) is false )
8283 {
8384 Error ( "Unable to trigger challenges without an auth token!" ) ;
8485 return null ;
@@ -89,6 +90,7 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
8990 } ;
9091
9192 AddRealmForDomain ( domain , parameters ) ;
93+ AddCustomParameters ( customParameters , parameters ) ;
9294
9395 string response = await SendRequest ( "/validate/triggerchallenge" , parameters , "POST" , headers , cancellationToken ) ;
9496 PIResponse ? ret = PIResponse . FromJSON ( response , this ) ;
@@ -100,16 +102,18 @@ public PrivacyIDEA(string url, string userAgent, bool sslVerify = true)
100102 /// Check if the challenge for the given transaction id has been answered yet. This is done using the /validate/polltransaction endpoint.
101103 /// </summary>
102104 /// <param name="transactionid"></param>
105+ /// <param name="customParameters">optional custom parameters to include</param>
103106 /// <param name="cancellationToken">optional</param>
104107 /// <returns>true if challenge was answered. false if not or error</returns>
105- public async Task < bool > PollTransaction ( string transactionid , CancellationToken cancellationToken = default )
108+ public async Task < bool > PollTransaction ( string transactionid , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
106109 {
107110 if ( string . IsNullOrEmpty ( transactionid ) is false )
108111 {
109112 var parameters = new Dictionary < string , string >
110113 {
111114 { "transaction_id" , transactionid }
112115 } ;
116+ AddCustomParameters ( customParameters , parameters ) ;
113117
114118 string response = await SendRequest ( "/validate/polltransaction" , parameters , "GET" , new List < KeyValuePair < string , string > > ( ) , cancellationToken ) ;
115119
@@ -137,11 +141,12 @@ public async Task<bool> PollTransaction(string transactionid, CancellationToken
137141 /// </summary>
138142 /// <param name="user">username</param>
139143 /// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
144+ /// <param name="customParameters">optional custom parameters to include</param>
140145 /// <param name="cancellationToken">optional</param>
141146 /// <returns>true if token exists. false if not or error</returns>
142- public async Task < bool > UserHasToken ( string user , string ? domain = null , CancellationToken cancellationToken = default )
147+ public async Task < bool > UserHasToken ( string user , string ? domain = null , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
143148 {
144- if ( await GetAuthToken ( cancellationToken ) is false )
149+ if ( await GetAuthToken ( customParameters , cancellationToken ) is false )
145150 {
146151 Error ( "Unable to lookup tokens without an auth token!" ) ;
147152 return false ;
@@ -151,6 +156,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
151156 { "user" , user }
152157 } ;
153158 AddRealmForDomain ( domain , parameters ) ;
159+ AddCustomParameters ( customParameters , parameters ) ;
154160
155161 string response = await SendRequest ( "/token/" , parameters , "GET" , new List < KeyValuePair < string , string > > ( ) , cancellationToken ) ;
156162 if ( string . IsNullOrEmpty ( response ) )
@@ -174,9 +180,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
174180 /// </summary>
175181 /// <param name="user">username</param>
176182 /// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
183+ /// <param name="customParameters">optional custom parameters to include</param>
177184 /// <param name="cancellationToken">optional</param>
178185 /// <returns>PIEnrollResponse object or null on error</returns>
179- public async Task < PIEnrollResponse ? > TokenInit ( string user , string ? domain = null , CancellationToken cancellationToken = default )
186+ public async Task < PIEnrollResponse ? > TokenInit ( string user , string ? domain = null , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
180187 {
181188 var parameters = new Dictionary < string , string >
182189 {
@@ -185,6 +192,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
185192 { "genkey" , "1" }
186193 } ;
187194 AddRealmForDomain ( domain , parameters ) ;
195+ AddCustomParameters ( customParameters , parameters ) ;
188196
189197 string response = await SendRequest ( "/token/init" , parameters , "POST" , new List < KeyValuePair < string , string > > ( ) , cancellationToken ) ;
190198 return PIEnrollResponse . FromJSON ( response , this ) ;
@@ -200,9 +208,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
200208 /// <param name="transactionid">optional transaction id to refer to a challenge</param>
201209 /// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
202210 /// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
211+ /// <param name="customParameters">optional custom parameters to include</param>
203212 /// <param name="cancellationToken">optional</param>
204213 /// <returns>PIResponse object or null on error</returns>
205- public async Task < PIResponse ? > ValidateCheck ( string user , string otp , string ? transactionid = null , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , CancellationToken cancellationToken = default )
214+ public async Task < PIResponse ? > ValidateCheck ( string user , string otp , string ? transactionid = null , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
206215 {
207216 var parameters = new Dictionary < string , string >
208217 {
@@ -216,6 +225,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
216225 }
217226
218227 AddRealmForDomain ( domain , parameters ) ;
228+ AddCustomParameters ( customParameters , parameters ) ;
219229
220230 string response = await SendRequest ( "/validate/check" , parameters , "POST" , headers , cancellationToken ) ;
221231 return PIResponse . FromJSON ( response , this ) ;
@@ -231,9 +241,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
231241 /// <param name="origin">origin also returned by the browser</param>
232242 /// <param name="domain">optional domain which can be mapped to a privacyIDEA realm</param>
233243 /// <param name="headers">optional headers which can be forwarded to the privacyIDEA server</param>
244+ /// <param name="customParameters">optional custom parameters to include</param>
234245 /// <param name="cancellationToken">optional</param>
235246 /// <returns>PIResponse object or null on error</returns>
236- public async Task < PIResponse ? > ValidateCheckWebAuthn ( string user , string transactionID , string webAuthnSignResponse , string origin , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , CancellationToken cancellationToken = default )
247+ public async Task < PIResponse ? > ValidateCheckWebAuthn ( string user , string transactionID , string webAuthnSignResponse , string origin , string ? domain = null , List < KeyValuePair < string , string > > ? headers = null , Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
237248 {
238249 if ( string . IsNullOrEmpty ( user ) || string . IsNullOrEmpty ( transactionID ) || string . IsNullOrEmpty ( webAuthnSignResponse ) || string . IsNullOrEmpty ( origin ) )
239250 {
@@ -279,6 +290,7 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
279290 }
280291
281292 AddRealmForDomain ( domain , parameters ) ;
293+ AddCustomParameters ( customParameters , parameters ) ;
282294
283295 // The origin has to be set in the header for WebAuthn authentication
284296 headers ??= new List < KeyValuePair < string , string > > ( ) ;
@@ -298,9 +310,10 @@ public async Task<bool> UserHasToken(string user, string? domain = null, Cancell
298310 /// Gets an auth token from the privacyIDEA server using the service account.
299311 /// Afterward, the token is set as the default authentication header for the HttpClient.
300312 /// </summary>
313+ /// <param name="customParameters">optional custom parameters to include</param>
301314 /// <param name="cancellationToken">optional</param>
302315 /// <returns>true if success, false otherwise</returns>
303- private async Task < bool > GetAuthToken ( CancellationToken cancellationToken = default )
316+ private async Task < bool > GetAuthToken ( Dictionary < string , string > ? customParameters = null , CancellationToken cancellationToken = default )
304317 {
305318 if ( string . IsNullOrEmpty ( _serviceUser ) || string . IsNullOrEmpty ( _servicePass ) )
306319 {
@@ -320,6 +333,8 @@ private async Task<bool> GetAuthToken(CancellationToken cancellationToken = defa
320333 parameters . Add ( "realm" , _serviceRealm ) ;
321334 }
322335
336+ AddCustomParameters ( customParameters , parameters ) ;
337+
323338 string response = await SendRequest ( "/auth" , parameters , "POST" , null , cancellationToken ) ;
324339
325340 if ( string . IsNullOrEmpty ( response ) )
@@ -351,13 +366,15 @@ private async Task<bool> GetAuthToken(CancellationToken cancellationToken = defa
351366 }
352367 }
353368
354- public void SetServiceAccount ( string user , string pass , string realm = "" )
355- {
356- _serviceUser = user ;
357- _servicePass = pass ;
358- _serviceRealm = realm ?? "" ;
359- }
360-
369+ /// <summary>
370+ /// Sends a request to the privacyIDEA server.
371+ /// </summary>
372+ /// <param name="endpoint">API endpoint</param>
373+ /// <param name="parameters">POST/GET parameters</param>
374+ /// <param name="method">HTTP method: POST or GET</param>
375+ /// <param name="headers">optional headers to add to the request</param>
376+ /// <param name="cancellationToken">optional</param>
377+ /// <returns>response string</returns>
361378 private Task < string > SendRequest ( string endpoint , Dictionary < string , string > parameters , string method ,
362379 List < KeyValuePair < string , string > > ? headers = null , CancellationToken cancellationToken = default )
363380 {
@@ -415,6 +432,19 @@ private Task<string> SendRequest(string endpoint, Dictionary<string, string> par
415432 return Task . FromResult ( ret ) ;
416433 }
417434
435+ /// <summary>
436+ /// Sets the service account credentials.
437+ /// </summary>
438+ /// <param name="user">username</param>
439+ /// <param name="pass">password</param>
440+ /// <param name="realm">optional realm</param>
441+ public void SetServiceAccount ( string user , string pass , string realm = "" )
442+ {
443+ _serviceUser = user ;
444+ _servicePass = pass ;
445+ _serviceRealm = realm ?? "" ;
446+ }
447+
418448 /// <summary>
419449 /// Evaluates which realm to use for a given domain and adds it to the parameter dictionary.
420450 /// The realm mapping takes precedence over the general realm that can be set. If no realm is found, the parameter is omitted.
@@ -458,6 +488,27 @@ private void AddRealmForDomain(string? domain, Dictionary<string, string> parame
458488 }
459489 }
460490
491+ /// <summary>
492+ /// Adds custom parameters to request.
493+ /// </summary>
494+ /// <param name="customParameters">Dictionary of custom parameters to add.</param>
495+ /// <param name="parameters">The dictionary to add the parameters to.</param>
496+ private static void AddCustomParameters ( Dictionary < string , string > ? customParameters , Dictionary < string , string > parameters )
497+ {
498+ if ( customParameters != null )
499+ {
500+ foreach ( var attribute in customParameters )
501+ {
502+ parameters . Add ( attribute . Key , attribute . Value ) ;
503+ }
504+ }
505+ }
506+
507+ /// <summary>
508+ /// Converts a dictionary to a StringContent with application/x-www-form-urlencoded encoding.
509+ /// </summary>
510+ /// <param name="dict">The dictionary to convert.</param>
511+ /// <returns>A StringContent object with the encoded dictionary.</returns>
461512 internal static StringContent DictToEncodedStringContent ( Dictionary < string , string > dict )
462513 {
463514 StringBuilder sb = new ( ) ;
0 commit comments