@@ -173,12 +173,19 @@ def __init__(self, *args, **kwargs) -> None:
173173 self .session = session
174174 self ._ctx = Context (self )
175175
176- @requires ("2025.01.0-dev " )
176+ @requires ("2025.01.0" )
177177 def with_user_session_token (self , token : str ) -> Client :
178178 """Create a new Client scoped to the user specified in the user session token.
179179
180180 Create a new Client instance from a user session token exchange for an api key scoped to the
181- user specified in the token.
181+ user specified in the token (the user viewing your app). If running your application locally,
182+ a user session token will not exist, which will cause this method to result in an error needing
183+ to be handled in your application.
184+
185+ Depending on the type of application you are building, the user session token is retrieved in
186+ a variety of ways. For example, in Streamlit and Shiny applications, the token is stored in the
187+ context or session object headers using the `Posit-Connect-User-Session-Token` key. For API
188+ applications, the token is added to the request headers.
182189
183190 Parameters
184191 ----------
@@ -190,19 +197,69 @@ def with_user_session_token(self, token: str) -> Client:
190197 Client
191198 A new Client instance authenticated with an API key exchanged for the user session token.
192199
200+ Raises
201+ ------
202+ ValueError
203+ If the provided token is `None` or empty or if the exchange response is malformed.
204+ ClientError
205+ If the token exchange request with the Connect Server fails.
206+
193207 Examples
194208 --------
195- >>> from posit.connect import Client
196- >>> client = Client().with_user_session_token("my-user-session-token")
209+ ```python
210+ from posit.connect import Client
211+ client = Client().with_user_session_token("my-user-session-token")
212+ ```
213+
214+ Example using user session token from Shiny session:
215+ ```python
216+ from posit.connect import Client
217+ from shiny.express import render, session
218+
219+ client = Client()
220+
221+ @reactive.calc
222+ def visitor_client():
223+ ## read the user session token and generate a new client
224+ user_session_token = session.http_conn.headers.get(
225+ "Posit-Connect-User-Session-Token"
226+ )
227+ return client.with_user_session_token(user_session_token)
228+ @render.text
229+ def user_profile():
230+ # fetch the viewer's profile information
231+ return visitor_client().me
232+ ```
233+
234+ Example of when the visitor's token could not be retrieved (for
235+ example, if this app allows unauthenticated access) and handle
236+ that in cases where a token is expected.
237+ ```python
238+ from posit.connect import Client
239+ import requests
240+
241+ # Simulate request without header
242+ mock_request = requests.Request()
243+ visitor_client = None
244+ token = request.headers.get("Posit-Connect-User-Session-Token")
245+ if token:
246+ visitor_client = Client().with_user_session_token(token)
247+ else:
248+ print("This app requires a user session token to operate.")
249+ ```
197250 """
198- viewer_credentials = self .oauth .get_credentials (
251+ if token is None or token == "" :
252+ raise ValueError ("token must be set to non-empty string." )
253+
254+ visitor_credentials = self .oauth .get_credentials (
199255 token , requested_token_type = API_KEY_TOKEN_TYPE
200256 )
201- viewer_api_key = viewer_credentials .get ("access_token" )
202- if viewer_api_key is None :
203- raise ValueError ("Unable to retrieve viewer api key." )
204257
205- return Client (url = self .cfg .url , api_key = viewer_api_key )
258+ visitor_api_key = visitor_credentials .get ("access_token" , "" )
259+ if visitor_api_key == "" :
260+ raise ValueError ("Unable to retrieve token." )
261+
262+ return Client (url = self .cfg .url , api_key = visitor_api_key )
206263
207264 @property
208265 def content (self ) -> Content :
0 commit comments