You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unable to reassign the Authenticator property in v109 #2024
Describe the bug
The changes introduced by making the Authenticator on RestClient immutable makes it impossible to use the same RestClient for authentication and after successfully authentication setting the Authenticator for further method calls which require the client to be authenticated.
Working around this by initializing the RestClient with a JwtAuthenticator with an invalid token and using this to authenticate against the rest endpoint. Then after succesfully authenticating use SetBearerToken to set the token on the JwtAuthenticator for further method calls.
The docs suggests using a single instance for making frequent calls. If you are doing a single call for authentication purposes you can use a separate, one-time-use client instance.
You can also hold the authenticator instance inside your wrapper:
JwtAuthenticator _authenticator;
public CustomRestClient(string baseUrl) {
_authenticator = new JwtAuthenticator(EMPTY_TOKEN)
_restClient = new RestClient(new RestClientOptions(baseUrl) {
Authenticator = authenticator
});
}
and then call SetBearerToken when you have it.
However, if you look at my old Twitter OAuth2 authenticator sample, it makes a call on-demand using a separate client instance inside the authenticator. It allows you to have full control over the flow.
I think the main deficiency of the authenticator approach with tokens is that it doesn't support token refresh. I believe we need a separate issue to discuss most common scenarios. Getting the token using grant type, client id and secret is very common. But the OIDC token and authentication token, for example, work differently.
It would be great to document specific flows in that new issue to figure out a workable solution, as we all know that the IAuthenticator abstraction is too weak.
changed the title [-]Commit #7e0a1af breaks existing scenario of using a single RestClient[/-][+]Unable to reassign the Authenticator property in v109[/+]on Mar 13, 2023
The docs state that a single instance of RestClient should be used to prevent starvation.
That is true, but how can a single additional client instance only used for authentication (once) can cause starvation? The infamous issue with too many open sockets is caused by repeated creation of message handlers, which is not the case when we are talking about a single call to get a token.
I'd really like to find out how authenticators can be made better. Checking the status code for 401 was one of my thoughts too. If you have a proposal for the authentication flow and related changes in code, please write here.
Given how complex and fractured the authentication space is I think the only easy addition would be to add some assistance to the supplied Authenticator for 401 handling for expired/revoked tokens.
Maybe something like this: (pseudo-code)
<inside Execute method>:
If response status is 401:
if request included an auth header value:
add some additional state to pass to .Authenticate call to hint that it might be a re-auth (either due to expiration, or token revocation, the cause really isn't important of course)
if the returned token is different:
re-execute the same url with the new token from the authenticator
else:
nothing else to do, let the caller deal with the problem
Determining if the request has an auth header could be done a couple of different ways if more than just 'Authorization' needs to be supported.
Additionally, if Authenticators could somehow re-use the existing RestClient instance instead of needing to create another one to perform the authentication request with, that'd be nice as well but that seems like a tricky road to go down.
It seems like there should be a second method for authenticators: AuthenticateWithFailedResponse or something so that the Authenticator can choose to examine the content of the failed response for error conditions, decide to utilize a refresh token, or just re-request the token based on whatever OAuth flow that is being used.
So, the above pseudo-code might look like this instead:
<inside Execute method>:
If response status is 401:
if request included an auth header value:
call Authenticator.AuthenticateWithFailedRespond(RestResponse)
if the returned token is different:
re-execute the same url with the new token from the authenticator
return the new response
else:
nothing else to do, let the caller deal with the problem and just return the original response
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
I have a specific use case in which I first have to use an OAuth1Authenticator via key and secret when logging in to a web API and then have to do another authentication with an OAuth1Authenticator. The access then takes place via another OAuth1Authenticator.
So I first need an Access Token and then a RequestToken etc.
It's always possible to use one client instance to do the first call, and then use the result of that call on the regular instance. The first instance gets disposed after making that single call. The Twitter authenticator example I made a while ago does exactly that.
The problem of any property being assignable after the client instance is created, whilst it is being used is that it by definition makes the client not thread-safe. Someone might just replace the authenticator for their single call. Meanwhile the other thread does the call expecting the original authenticator, and it all becomes a mess.
It seems to me that you could write an aggregated Authenticator by taking something similar to my updated authenticator sample authenticator and adding an overridden Authenticate method and determine what authenticator actually to use based on the contents of the RestRequest, but that might be too much fragility or pain and suffering... However, that'd be up to you to decide...
Additionally, the RestSharp request object has an Authenticator property that you can override the RestClient Options Authenticator property value for any request that needs to use a differing Authenticator from the default.
Activity
Balkoth commentedon Mar 13, 2023
Working around this by initializing the
RestClientwith aJwtAuthenticatorwith an invalid token and using this to authenticate against the rest endpoint. Then after succesfully authenticating useSetBearerTokento set the token on theJwtAuthenticatorfor further method calls.I don't know if any other, than the rest endpoint i currently use, support this behaviour, so use with caution.
alexeyzimarev commentedon Mar 13, 2023
The docs suggests using a single instance for making frequent calls. If you are doing a single call for authentication purposes you can use a separate, one-time-use client instance.
You can also hold the authenticator instance inside your wrapper:
and then call
SetBearerTokenwhen you have it.However, if you look at my old Twitter OAuth2 authenticator sample, it makes a call on-demand using a separate client instance inside the authenticator. It allows you to have full control over the flow.
I think the main deficiency of the authenticator approach with tokens is that it doesn't support token refresh. I believe we need a separate issue to discuss most common scenarios. Getting the token using grant type, client id and secret is very common. But the OIDC token and authentication token, for example, work differently.
It would be great to document specific flows in that new issue to figure out a workable solution, as we all know that the
IAuthenticatorabstraction is too weak.[-]Commit #7e0a1af breaks existing scenario of using a single RestClient[/-][+]Unable to reassign the Authenticator property in v109[/+]alexeyzimarev commentedon Mar 29, 2023
One comment about this:
That is true, but how can a single additional client instance only used for authentication (once) can cause starvation? The infamous issue with too many open sockets is caused by repeated creation of message handlers, which is not the case when we are talking about a single call to get a token.
rassilon commentedon Mar 29, 2023
@alexeyzimarev , while migrating a C# REST library to 109 I ended up with this authenticator based on your old twitter example.
It is assisted with this additional logic in the core call to RestSharpClient Execute method:
The authenticator uses an AsyncLock to ensure only one additional RestClient gets created for the new token aquistion.
FYI...
alexeyzimarev commentedon Mar 30, 2023
I'd really like to find out how authenticators can be made better. Checking the status code for 401 was one of my thoughts too. If you have a proposal for the authentication flow and related changes in code, please write here.
rassilon commentedon Mar 30, 2023
Given how complex and fractured the authentication space is I think the only easy addition would be to add some assistance to the supplied Authenticator for 401 handling for expired/revoked tokens.
Maybe something like this: (pseudo-code)
Determining if the request has an auth header could be done a couple of different ways if more than just 'Authorization' needs to be supported.
rassilon commentedon Mar 30, 2023
Additionally, if Authenticators could somehow re-use the existing RestClient instance instead of needing to create another one to perform the authentication request with, that'd be nice as well but that seems like a tricky road to go down.
rassilon commentedon Mar 31, 2023
After checking out: https://www.oauth.com/oauth2-servers/making-authenticated-requests/refreshing-an-access-token/
It seems like there should be a second method for authenticators: AuthenticateWithFailedResponse or something so that the Authenticator can choose to examine the content of the failed response for error conditions, decide to utilize a refresh token, or just re-request the token based on whatever OAuth flow that is being used.
So, the above pseudo-code might look like this instead:
stale commentedon May 22, 2023
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
repo-ranger commentedon May 22, 2023
abc123xxx commentedon Mar 5, 2024
Is there anything new on the subject?
I also need to be able to change the authenticator after creating the RestClient.
rassilon commentedon Mar 5, 2024
Could you explain your use case a little here? I'm not sure why you'd need more than one authenticator after creating the RestClient.
abc123xxx commentedon Mar 6, 2024
I have a specific use case in which I first have to use an OAuth1Authenticator via key and secret when logging in to a web API and then have to do another authentication with an OAuth1Authenticator. The access then takes place via another OAuth1Authenticator.
So I first need an Access Token and then a RequestToken etc.
Here is the call chain:
alexeyzimarev commentedon Mar 6, 2024
It's always possible to use one client instance to do the first call, and then use the result of that call on the regular instance. The first instance gets disposed after making that single call. The Twitter authenticator example I made a while ago does exactly that.
The problem of any property being assignable after the client instance is created, whilst it is being used is that it by definition makes the client not thread-safe. Someone might just replace the authenticator for their single call. Meanwhile the other thread does the call expecting the original authenticator, and it all becomes a mess.
rassilon commentedon Mar 7, 2024
It seems to me that you could write an aggregated Authenticator by taking something similar to my updated authenticator sample authenticator and adding an overridden Authenticate method and determine what authenticator actually to use based on the contents of the RestRequest, but that might be too much fragility or pain and suffering... However, that'd be up to you to decide...
rassilon commentedon Mar 12, 2024
Additionally, the RestSharp request object has an Authenticator property that you can override the RestClient Options Authenticator property value for any request that needs to use a differing Authenticator from the default.
abc123xxx commentedon Mar 13, 2024
ok, the request authenticator is the solution. Thank you for the tip.