The content in this guide operates under the assumption that all of the necessary setup in the OAuth integrations section of the Admin Guide has already been completed.
Overview
Applications hosted on Posit Connect can be configured to acquire OAuth access tokens which allow publishers to author content with access to third-party protected resources. These access tokens are curated and provided to published content by Connect, but their associated permissions are defined externally. This allows data administrators to acquire and reuse their organization’s existing data access policies from a single centralized location.
Connect supports two types of OAuth integrations which can be used in published content. Content can have multiple associated integrations. It is encouraged to utilize the dashboard, API, or SDK to identify the integration GUIDs you have associated with your content to inform the credential exchange endpoint which integration’s credentials you need using the audience parameter. See below for an example of this.
Viewer integrations allow publishers to acquire and use the content viewer’s credentials when accessing protected resources, providing the viewer with a personalized view of the content. Viewers are only able to see the data they have been granted access to in the external system. Viewer integrations only support interactive content types. Content with access type Anyone - no login required and Share Links are not supported by Viewer integrations. Visitor API Key is a sub-type of Viewer integrations and adheres to the same supported content types and access patterns as its parent.
Service Account integrations allow publishers to acquire and use the credentials of a service account or service principal when accessing protected resources. Every viewer will have the same experience when interacting with the content. Service account integrations support both interactive and rendered content types. Additionally, access patterns using Anyone - no login required and Share Links are supported by Service Account integrations.
When authoring content that requires access to protected resources, publishers should use the Posit SDK for Python, connectapi for R, or call the Connect Server API’s /v1/oauth/integrations/credentials endpoint directly to obtain an OAuth access token.
Security considerations
Publishers must take care to ensure that access tokens are not leaked through content logs, cached incorrectly by the content, or otherwise misused. Each Python and R web application framework exposes session/application state differently.
It is up to the publisher to understand in which contexts it is appropriate to request and use OAuth credentials.
Obtaining a viewer OAuth access token
In order for content to obtain a viewer’s OAuth access token, the following criteria must be met:
The content must be deployed to Connect.
The content must be associated with one or more Viewer OAuth integrations.
The viewer must visit the running content on Connect.
When content is running on Connect, a short-lived user-session-token is attached to the Posit-Connect-User-Session-Token HTTP header for all interactive content requests. The user-session-token serves as the subject_token in the credential exchange request.
The following examples demonstrate how to perform a credential exchange in order to obtain the viewer’s access token from Connect.
from posit importconnectfrom shiny.express import render, session# initialize the Connect client# runs once per startupclient =connect.Client()# runs once per session@render.textdef access_token():# read the user-session-token header user_session_token = session.http_conn.headers.get("Posit-Connect-User-Session-Token")# fetch the viewer's access token# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterreturn client.oauth.get_credentials(user_session_token, audience="<integration-guid>").get("access_token")
Note
This example uses the Shiny for R web application framework and the connectapi package to perform the credential exchange.
library(connectapi)library(shiny)ui <-fluidPage(textOutput("access_token"))server <-function(input, output, session) {# initialize the Connect client client <-connect()# read the user-session-token header user_session_token <- session$request$HTTP_POSIT_CONNECT_USER_SESSION_TOKEN output$access_token <-renderText({# fetch the viewer's access token# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameter credentials <-get_oauth_credentials(client, user_session_token, audience="<integration-guid>") credentials$access_token })}# start the Shiny appshinyApp(ui = ui, server = server)
Access tokens are short-lived, usually expiring within 24 hours of when they were issued. Content that utilizes access tokens must be resilient to errors that can occur when the access token expires. To simplify this for content authors, Connect is responsible for access token refresh. Requesting a viewer’s access token from the /v1/oauth/integrations/credentials endpoint always returns a refreshed access token.
Connect encrypts and stores the viewer’s access token and refresh token with their OAuth session. If the viewer’s access token is valid, it is returned right away. If the viewer’s access token is expired (or about to expire) then Connect will attempt to refresh the OAuth Access Token first, before returning it to the requester (the content).
OAuth refresh tokens are never returned by Connect.
Obtaining a service account OAuth access token
In order for content to obtain a service account’s OAuth access token, the following criteria must be met:
The content must be deployed to Connect.
The content must be associated with one or more Service Account OAuth integrations.
When content is running on Connect, a short-lived content-session-token is set in the CONNECT_CONTENT_SESSION_TOKEN environment variable for all interactive and rendered content processes. The content-session-token serves as the subject_token in the credential exchange request.
The following examples demonstrate how to perform a credential exchange in order to obtain a service account access token from Connect.
This example uses the posit-python-sdk to perform the credential exchange.
from posit importconnectclient =connect.Client()# NOTE: get_content_credentials reads the `CONNECT_CONTENT_SESSION_TOKEN`# from the environment to complete the credential exchange.# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterprint(client.oauth.get_content_credentials(audience="<integration-guid>").get("access_token"))
Note
This example uses the connectapi package to perform the credential exchange.
library(connectapi)client <-connect()# NOTE: get_oauth_content_credentials reads the `CONNECT_CONTENT_SESSION_TOKEN`# from the environment to complete the credential exchange.# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterresp <-get_oauth_content_credentials(client, audience="<integration-guid>")resp$access_token
Connect does not acquire a refresh token when a Service Account credential exchange occurs. It also does not store the acquired access token in a session. On every Service Account credential exchange, Connect requests a new access token from the external system, so every request to the credential exchange endpoint will acquire a fresh access token.
Obtaining a visitor API Key
Visitor API Key integrations currently only support retrieving API keys for the Connect API. In order for content to obtain a visiting user’s Connect API key, the following criteria must be met:
The content must be deployed to Connect.
The content must be associated with one or more Visitor API Key integrations.
The viewer must visit the running content on Connect.
When setting up a Visitor API Key integration, administrators can limit the maximum role (Viewer, Publisher, or Administrator) for generated API keys. The actual API key role will be the more restrictive of: (1) this configured maximum and (2) the visiting user’s current Connect role. For example, if the maximum role is Publisher, but visiting user has a Viewer role, the API key role will be Viewer.
Outside of the visitor’s account role, the API key allows applications to identify the visiting user’s account, inherit their content access ownership and permissions, and more.
When content is running on Connect, a short-lived user-session-token is attached to the Posit-Connect-User-Session-Token HTTP header for all interactive content requests. The user-session-token serves as the subject_token in the credential exchange request.
The following examples demonstrate how to create a Connect API client from a user session token using our Python and R SDKs. The generated client is then able to interact with the Connect server on behalf of the user viewing the application.
This example uses the Shiny for Python web application framework and the posit-python-sdk to generate a Connect client scoped to user viewing the application.
from posit importconnectfrom shiny.express import render, session# initialize the Connect client# runs once per startupclient =connect.Client()@reactive.calcdef visitor_client():## read the user session token and generate a new client user_session_token = session.http_conn.headers.get("Posit-Connect-User-Session-Token")# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterreturn client.with_user_session_token(user_session_token, audience="<integration-guid>")@render.textdef user_profile():# fetch the viewer's profile informationreturn visitor_client().me
This example uses the Shiny for R web application framework and the connectapi package to generate a Connect client scoped to user viewing the application.
library(shiny)library(connectapi)ui <-fluidPage("Publisher API key",verbatimTextOutput("default_api_key"),"Visitor API Key",verbatimTextOutput("visitor_api_key"))server <-function(input, output, session) {# Create a publisher client for comparison. publisher_client <-connect()# Read the user-session-token header and create a viewer client user_session_token <- session$request$HTTP_POSIT_CONNECT_USER_SESSION_TOKEN# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameter visitor_client <-connect(token = user_session_token, audience ="<integration-guid>") output$default_api_key <-renderText(publisher_client$api_key) output$visitor_api_key <-renderText(visitor_client$api_key)}shinyApp(ui, server)
Connect API keys are ephemeral and associated with the application lifecycle itself. When an application shuts down or is restarted, any existing ephemeral API keys are cleaned up. In the event that a Connect client generated from a user session token becomes unable to authenticate with the Connect server, it is recommended that the publisher handle this gracefully by refreshing that client with the latest available user session token.
Obtaining viewer AWS credentials
In order for content to assume an AWS IAM role on behalf of the viewer and retrieve temporary credentials, the following criteria must be met:
The content must be deployed to Connect.
The content must be associated with one or more Viewer AWS integrations.
The viewer must visit the running content on Connect.
When content is running on Connect, a short-lived user-session-token is attached to the Posit-Connect-User-Session-Token HTTP header for all interactive content requests. The user-session-token serves as the subject_token in the credential exchange request.
The following examples demonstrate how to perform a credential exchange in order to obtain the viewer’s credentials from Connect.
from posit importconnectfrom posit.connect.external import awsfrom shiny.express import render, session# initialize the Connect client# runs once per startupclient =connect.Client()# runs once per session@render.textdef aws_creds():# read the user-session-token header user_session_token = session.http_conn.headers.get("Posit-Connect-User-Session-Token")# fetch the AWS credentials# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterreturn aws.get_credentials(client, user_session_token, audience ="<integration-guid>")
Note
This example uses the Shiny for R web application framework and the connectapi package to perform the credential exchange.
library(connectapi)library(shiny)ui <-fluidPage(textOutput("aws_creds"))server <-function(input, output, session) {# initialize the Connect client client <-connect()# read the user-session-token header user_session_token <- session$request$HTTP_POSIT_CONNECT_USER_SESSION_TOKEN output$aws_creds <-renderText({# fetch the aws credentials# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterget_aws_credentials(client, user_session_token, audience="<integration-guid>") })}# start the Shiny appshinyApp(ui = ui, server = server)
When requesting AWS credentials, Connect uses the AssumeRoleWithWebIdentity API call to exchange an OIDC-compliant token for temporary AWS credentials. This process involves:
Connect uses the administrator-defined identity provider’s token to authenticate to AWS
AWS validates the token by retrieving the token’s public signing key hosted by the identity provider
AWS returns temporary credentials (access key ID, secret access key, session token, and expiration) for the role configured by the Connect administrator
Your content assumes the IAM role by using the temporary credentials
The temporary credentials are typically valid for 1 hour, though this is configured by your AWS and Connect administrators. Content must handle credential expiration gracefully. Connect helps manage this by automatically requesting fresh credentials when needed through the /v1/oauth/integrations/credentials endpoint.
Connect securely provides these temporary AWS credentials to the content. The credentials are never stored permanently - they are generated on-demand when requested by the content.
For security, the raw OIDC tokens used in the exchange are never exposed to the content itself.
Obtaining service account AWS credentials
In order for content to assume an AWS IAM role and retrieve temporary credentials, the following criteria must be met:
The content must be deployed to Connect.
The content must be associated with one or more Service Account AWS integrations.
When content is running on Connect, a short-lived content-session-token is set in the CONNECT_CONTENT_SESSION_TOKEN environment variable for all interactive and rendered content processes. The content-session-token serves as the subject_token in the credential exchange request.
The following examples demonstrate how to perform a credential exchange in order to obtain temporary AWS credentials from Connect.
This example uses the posit-python-sdk to perform the credential exchange.
from posit importconnectfrom posit.connect.external import awsclient =connect.Client()# NOTE: get_content_credentials reads the `CONNECT_CONTENT_SESSION_TOKEN`# from the environment to complete the credential exchange.# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterprint(aws.get_content_credentials(client, audience="<integration-guid>"))
Note
This example uses the connectapi package to perform the credential exchange.
library(connectapi)client <-connect()# NOTE: get_oauth_content_credentials reads the `CONNECT_CONTENT_SESSION_TOKEN`# from the environment to complete the credential exchange.# if you have multiple integrations associated with your content pass the integration GUID# using the audience parameterresp <-get_aws_content_credentials(client, audience="<integration-guid>")
When requesting AWS credentials, Connect uses the AssumeRoleWithWebIdentity API call to exchange an OIDC-compliant token for temporary AWS credentials. This requires the following steps:
Connect creates a temporary Workload Identity token that is OIDC-compliant and identifies your content. This token is sent to AWS.
AWS validates the token by retrieving the token’s public signing key hosted by Connect
AWS returns temporary credentials (access key ID, secret access key, session token, and expiration) for the role configured by the Connect administrator
Your content assumes the IAM role by using the temporary credentials
The temporary credentials are typically valid for 1 hour, though this is configured by your AWS and Connect administrators. Content must handle credential expiration gracefully. Connect helps manage this by automatically requesting fresh credentials when needed through the /v1/oauth/integrations/credentials endpoint.
Connect securely provides these temporary AWS credentials to the content. The credentials are never stored permanently - they are generated on-demand when requested by the content.
Local development
OAuth integrations can only be used to obtain an OAuth access token when the content is running on a Connect server, so testing local changes to content can be challenging. A good strategy for authoring content that uses OAuth integrations is to first check whether the content is running on a Connect server, and then fall-back to the developer’s locally configured credentials when it is not. All content running on a Connect server has the following environment variable set: POSIT_PRODUCT=CONNECT. This environment variable can be used by the content to determine if it is running on a Connect server.
Once the content has been deployed to Connect, the publisher associates OAuth integrations with their content.
This is accomplished using the dashboard by visiting the Access tab in the Content Settings pane, where the publisher will see an Integrations section with a Select integrations button.
Clicking this button will open a modal which allows the Connect publisher to select one or more OAuth integrations.
For rendered content, the list of available integrations in the modal will be filtered to only include Service Account integrations. Viewer integrations are not supported for rendered content.
After selecting one or more integrations and saving, a panel containing information about the integrations appears below the Select integrations button.
Viewer OAuth integrations also provide a button to log in to each OAuth integration.
Alternatively, the example below uses curl and the Connect Server API to associate OAuth integrations with an existing piece of deployed content in Connect.
Note
Replace connect.example.org with the address of the Connect server.
Terminal
# list all available oauth integrationscurl-H"Authorization: Key ${CONNECT_API_KEY}"\-XGET https://connect.example.org/__api__/v1/oauth/integrations# assign oauth integrations to a piece of contentcurl-H"Authorization: Key ${CONNECT_API_KEY}"\-XPUT https://connect.example.org/__api__/v1/content/<content-guid>/oauth/integrations/associations \--data'[ {"oauth_integration_guid": "<oauth-integration-guid-1>"}, {"oauth_integration_guid": "<oauth-integration-guid-2>"} ]'
Obtaining credentials within content associated with multiple integrations
When content has mutiple associated integrations, the publisher is required to provide the audience to the credential exchange endpoint so that it can determine which integration you need credentials for. This field is set with an integration GUID that is either:
associated to your content through an API call or manually adding them through the dashboard
a Connect API integration which is globally accessible to all interactive content
There are helpers in the SDKs that make it easy to find the integrations you are looking for without needing to explicitly know or set a GUID in your codebase.
This example uses the connectapi package to perform the credential exchange.
library(connectapi)client <-connect()content_associations_endpoint <-paste0("v1/content/", Sys.getenv("CONNECT_CONTENT_GUID"), "/oauth/integrations/associations")associations <- client$GET(content_associations_endpoint)# Get credentials for each associated integrationintegration1_creds <-get_oauth_content_credentials( client,audience = associations[[1]]$oauth_integration_guid)integration2_creds <-get_aws_credentials( client, audience = associations[[2]]$oauth_integration_guid)
Viewing content on Connect
Viewer OAuth integrations
Connect users who have the Viewer permission on the content item receive a personalized view when they visit a deployed application which uses Viewer OAuth integrations.
Upon visiting a piece of content that is associated with one or more Viewer OAuth integrations for the first time, if the user is accessing the content through the Connect dashboard, they are prompted to log in to each one.
The login modal only appears if the user is not already logged in to an integration and the content settings pane is closed. The Login button is also available on the Access tab of the Content Settings pane for each viewer integration.
When interacting with the content in open-solo mode and the viewer is not already logged in to an integration, then the viewer is automatically redirected to the login URL of the OAuth integrations in order to initialize their respective OAuth sessions.
OAuth sessions
An OAuth session holds metadata about the Connect user’s OAuth tokens. An OAuth session is created when a Connect user visits the /login endpoint of any OAuth integration. The login endpoint has the form: /__oauth__/integrations/<oauth-integration-guid>/login. In order to populate the OAuth session with an access token and refresh token, the user must complete the login flow by visiting the /login endpoint and successfully authenticating to the external service. After authenticating to the external service, the user is redirected back to Connect.
The user’s OAuth session and all OAuth tokens are deleted when the Connect user visits the /logout endpoint. The logout endpoint has the form: /__oauth__/integrations/<oauth-integration-guid>/logout.
OAuth sessions can also be managed using the /v1/oauth/sessions API endpoints. By default, Connect users can only manage their own OAuth sessions. Connect administrators can manage OAuth sessions of all Connect users. Managing an OAuth session does not allow the user to view OAuth tokens associated with the OAuth session. Access tokens are exposed only by the credential exchange endpoint.
OAuth refresh tokens are never returned by Connect.
An authenticated user in Connect can have only one OAuth session per Viewer OAuth integration. A given OAuth integration can be shared by multiple content items on Connect. The user only needs to log in to an OAuth integration once and the OAuth session remains valid for all pieces of content that use that OAuth integration. A user is considered to be logged in to a Viewer OAuth integration if there is an OAuth refresh token associated with the OAuth session.
A content viewer must visit a piece of content in order for the content to obtain the viewer’s access token. Simply associating an OAuth integration with a piece of content is not enough for the content to obtain a user’s access token.
This limitation prevents a content item from obtaining an arbitrary user’s access token, even when that user is assigned the Viewer role on the content. See the Admin guide’s security documentation for additional details.
Service account OAuth integrations
Connect users who have the Viewer permission on the content item have access to the permissions of the configured service accounts when using Service Account integrations. This means that the data will look the same for each user.
Because viewers are not granting access to their own OAuth tokens, but are instead using those of a Service Account, there is no login flow for the user to go through when they interact with the content. There are no login buttons, and the open solo view does not perform an automatic redirect. There are no OAuth sessions, because viewer OAuth credentials are not being maintained by Connect.
Instead, whenever the content executes, either because a viewer is interacting with the content or a render request is issued, Connect uses the service account credentials configured in the integration to request a fresh OAuth access token from the third-party service. Connect only performs this request for content which has (a) been associated with a Service Account integration, and (b) is actively executing, either as an interactive web application or a report render.