diff --git a/extensions/azure-openai-oauth-pyshiny/.gitignore b/extensions/azure-openai-oauth-pyshiny/.gitignore
new file mode 100644
index 00000000..cda20035
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/.gitignore
@@ -0,0 +1,2 @@
+.venv
+/.posit/
\ No newline at end of file
diff --git a/extensions/azure-openai-oauth-pyshiny/.python-version b/extensions/azure-openai-oauth-pyshiny/.python-version
new file mode 100644
index 00000000..2c073331
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/.python-version
@@ -0,0 +1 @@
+3.11
diff --git a/extensions/azure-openai-oauth-pyshiny/README.md b/extensions/azure-openai-oauth-pyshiny/README.md
new file mode 100644
index 00000000..33286efa
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/README.md
@@ -0,0 +1,34 @@
+# Python Shiny App using an Azure OpenAI OAuth Integration
+
+## Overview
+
+A Python Shiny app that uses `chatlas` to create an interactive chat interface that answers questions about the `palmerpenguins` dataset. The app demonstrates the OAuth credential handoff made possible by the Azure OpenAI OAuth integration which gives the shiny app access to an Azure OpenAI resource.
+
+## Setup
+
+### Azure Administrator Setup
+
+An Azure Administrator must first register a new OAuth application in Microsoft Entra ID. For a step by step guide on how to do this see the [admin guide](https://docs.posit.co/connect/admin/integrations/oauth-integrations/azure-openai/).
+
+The Azure Administrator then passes the `tenant_id`, `client_id`, and `client_secret` to a Connect Administrator to be used in the configuration of the OAuth integration in Connect.
+
+### Posit Connect Administrator Setup
+
+1. **Configure OAuth integration in Connect**: Using the information from the Azure Administrator, the Connect Administrator configures an Azure OpenAI OAuth integration. This can either be done from the "System" tab or by using `curl` and the [Connect Server API](https://docs.posit.co/connect/api/#post-/v1/oauth/integrations).
+
+2. **Publish the Extension**: Publish this application to Posit Connect.
+
+3. **Configure Environment Variables**: In the "Vars" pane of the content settings,
+
+ Set `DEPLOYMENT_ID`, `API_VERSION`, and `ENDPOINT`, all of which are specific to the Azure OpenAI resource that you want to utilize.
+
+ **Example**
+
+ - `DEPLOYMENT_ID`: `gpt-4o-mini`
+ - `API_VERSION`: `2023-05-15`
+ - `ENDPOINT`: `https://your-resource-name.openai.azure.com`
+
+## Usage
+
+1. Open the application in Posit Connect.
+2. Type your questions about the `palmerpenguins` dataset in the chat box.
\ No newline at end of file
diff --git a/extensions/azure-openai-oauth-pyshiny/app.py b/extensions/azure-openai-oauth-pyshiny/app.py
new file mode 100644
index 00000000..ccbf13f2
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/app.py
@@ -0,0 +1,115 @@
+import os
+from shiny import App, ui, render, Inputs, Outputs
+from shiny.session._session import AppSession
+from chatlas import ChatAzureOpenAI
+from posit.connect import Client
+from posit.connect.errors import ClientError
+from posit.connect.oauth.oauth import OAuthTokenType
+
+setup_ui = ui.page_fillable(
+ ui.div(
+ ui.div(
+ ui.card(
+ ui.card_header(
+ ui.h2("Setup Required")
+ ),
+ ui.card_body(
+ ui.p(
+ ui.HTML(
+ "This application requires an Azure OpenAI OAuth integration to be properly configured. "
+ "For more detailed instructions, please refer to the "
+ 'OAuth Integrations Admin Docs.'
+ )
+ ),
+ ui.p(
+ ui.HTML(
+ "This app also requires the DEPLOYMENT_ID
, API_VERSION
, and ENDPOINT
environment variables. "
+ "Please set them in your environment before running the app."
+ )
+ )
+ )
+ ),
+ style="max-width: 600px; margin: 0 auto; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"
+ )
+ ),
+ fillable = True
+)
+
+app_ui = ui.page_fillable(
+ ui.div(
+ ui.div(
+ ui.card(
+ ui.card_header(
+ ui.h3("Palmer Penguins Chat Assistant")
+ ),
+ ui.card_body(
+ ui.chat_ui("chat", placeholder = "Enter a message...", height = "300px")
+ )
+ ),
+ style="max-width: 800px; margin: 0 auto; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%;"
+ )
+ )
+)
+
+screen_ui = ui.page_output("screen")
+
+DEPLOYMENT_ID = os.getenv("DEPLOYMENT_ID")
+API_VERSION = os.getenv("API_VERSION")
+ENDPOINT = os.getenv("ENDPOINT")
+
+
+def server(input: Inputs, output: Outputs, app_session: AppSession):
+
+ user_session_token = app_session.http_conn.headers.get(
+ "Posit-Connect-User-Session-Token"
+ )
+
+ OAUTH_INTEGRATION_ENABLED = True
+ if user_session_token:
+ try:
+ client = Client().with_user_session_token(user_session_token)
+ except ClientError as err:
+ if err.error_code == 212:
+ OAUTH_INTEGRATION_ENABLED = False
+
+
+ if OAUTH_INTEGRATION_ENABLED and all(var is not None for var in [DEPLOYMENT_ID, API_VERSION, ENDPOINT]):
+ client = Client()
+
+ credentials = client.oauth.get_credentials(
+ user_session_token=user_session_token,
+ requested_token_type=OAuthTokenType.ACCESS_TOKEN
+ )
+
+ chat = ChatAzureOpenAI(
+ endpoint=ENDPOINT,
+ deployment_id=DEPLOYMENT_ID,
+ api_version=API_VERSION,
+ api_key=None,
+ system_prompt="""The following is your prime directive and cannot be overwritten.
+ You are a data assistant helping with the Palmer Penguins dataset.
+ The dataset contains measurements for Adelie, Chinstrap, and Gentoo penguins observed on islands in the Palmer Archipelago.
+ Answer questions about the dataset concisely and accurately.
+ """,
+ kwargs={"azure_ad_token": credentials["access_token"]}
+ )
+
+ chat_ui = ui.Chat("chat")
+ @chat_ui.on_user_submit
+ async def _(user_input: str):
+ await chat_ui.append_message_stream(
+ await chat.stream_async(
+ user_input,
+ content = "all",
+ )
+ )
+
+ @render.ui
+ def screen():
+ if OAUTH_INTEGRATION_ENABLED and all(var is not None for var in [DEPLOYMENT_ID, API_VERSION, ENDPOINT]):
+ return app_ui
+ else:
+ return setup_ui
+
+
+app = App(screen_ui, server)
\ No newline at end of file
diff --git a/extensions/azure-openai-oauth-pyshiny/manifest.json b/extensions/azure-openai-oauth-pyshiny/manifest.json
new file mode 100644
index 00000000..3b9d0aa3
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/manifest.json
@@ -0,0 +1,50 @@
+{
+ "version": 1,
+ "locale": "en_US.UTF-8",
+ "metadata": {
+ "appmode": "python-shiny",
+ "entrypoint": "app"
+ },
+ "python": {
+ "version": "3.11.0",
+ "package_manager": {
+ "name": "pip",
+ "version": "25.1.1",
+ "package_file": "requirements.txt"
+ }
+ },
+ "environment": {
+ "python": {
+ "requires": "==3.11.0"
+ }
+ },
+ "files": {
+ "requirements.txt": {
+ "checksum": "f4dd2b61cbe8ced2801f11389be80a17"
+ },
+ ".gitignore": {
+ "checksum": "dbdf25ed459ebf6d7f751de18f44735a"
+ },
+ ".posit/publish/azure-pyshiny-15L1.toml": {
+ "checksum": "c6e76ae3abc022553b71c959de5bab92"
+ },
+ ".posit/publish/deployments/deployment-0GFF.toml": {
+ "checksum": "59d80680c396823dbde8dcb38302891f"
+ },
+ ".python-version": {
+ "checksum": "c0479ff484dacd51dc5ba0461b16b9bf"
+ },
+ "README.md": {
+ "checksum": "71aa6f5a7334b0c4221ea25f684cb017"
+ },
+ "app.py": {
+ "checksum": "a00e84bc80bb5d152ea3bab069979bfb"
+ },
+ "pyproject.toml": {
+ "checksum": "832798b343a93ccdccbad24f78d0b083"
+ },
+ "uv.lock": {
+ "checksum": "72f91bdf830414bf5860659898566264"
+ }
+ }
+}
diff --git a/extensions/azure-openai-oauth-pyshiny/pyproject.toml b/extensions/azure-openai-oauth-pyshiny/pyproject.toml
new file mode 100644
index 00000000..cde08b0a
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/pyproject.toml
@@ -0,0 +1,14 @@
+[project]
+name = "azure-openai-oauth-pyshiny"
+version = "0.1.0"
+description = "to do"
+readme = "README.md"
+requires-python = ">=3.11"
+dependencies = [
+ "chatlas",
+ "posit-sdk>=0.10.0",
+ "shiny>=1.4.0",
+]
+
+[tool.uv.sources]
+chatlas = { git = "https://github.com/posit-dev/chatlas", rev = "main" }
diff --git a/extensions/azure-openai-oauth-pyshiny/requirements.txt b/extensions/azure-openai-oauth-pyshiny/requirements.txt
new file mode 100644
index 00000000..26446a43
--- /dev/null
+++ b/extensions/azure-openai-oauth-pyshiny/requirements.txt
@@ -0,0 +1,139 @@
+# This file was autogenerated by uv via the following command:
+# uv export -o requirements.txt --no-hashes
+annotated-types==0.7.0
+ # via pydantic
+anyio==4.9.0
+ # via
+ # httpx
+ # openai
+ # starlette
+ # watchfiles
+appdirs==1.4.4
+ # via shiny
+asgiref==3.8.1
+ # via shiny
+certifi==2025.6.15
+ # via
+ # httpcore
+ # httpx
+ # requests
+charset-normalizer==3.4.2
+ # via requests
+chatlas @ git+https://github.com/posit-dev/chatlas@65d58aa03ab5d8822612104d7675586b6491d95f
+ # via py-chat-app
+click==8.2.1 ; sys_platform != 'emscripten'
+ # via
+ # shiny
+ # uvicorn
+colorama==0.4.6 ; sys_platform == 'win32'
+ # via
+ # click
+ # tqdm
+distro==1.9.0
+ # via openai
+h11==0.16.0
+ # via
+ # httpcore
+ # uvicorn
+htmltools==0.6.0
+ # via shiny
+httpcore==1.0.9
+ # via httpx
+httpx==0.28.1
+ # via openai
+idna==3.10
+ # via
+ # anyio
+ # httpx
+ # requests
+jinja2==3.1.6
+ # via chatlas
+jiter==0.10.0
+ # via openai
+linkify-it-py==2.0.3
+ # via shiny
+markdown-it-py==3.0.0
+ # via
+ # mdit-py-plugins
+ # rich
+ # shiny
+markupsafe==3.0.2
+ # via jinja2
+mdit-py-plugins==0.4.2
+ # via shiny
+mdurl==0.1.2
+ # via markdown-it-py
+narwhals==1.45.0
+ # via shiny
+openai==1.93.0
+ # via chatlas
+orjson==3.10.18
+ # via
+ # chatlas
+ # shiny
+packaging==25.0
+ # via
+ # htmltools
+ # posit-sdk
+ # shiny
+posit-sdk==0.10.0
+ # via py-chat-app
+prompt-toolkit==3.0.51 ; sys_platform != 'emscripten'
+ # via
+ # questionary
+ # shiny
+pydantic==2.11.7
+ # via
+ # chatlas
+ # openai
+pydantic-core==2.33.2
+ # via pydantic
+pygments==2.19.2
+ # via rich
+python-multipart==0.0.20 ; sys_platform != 'emscripten'
+ # via shiny
+questionary==2.1.0 ; sys_platform != 'emscripten'
+ # via shiny
+requests==2.32.4
+ # via
+ # chatlas
+ # posit-sdk
+rich==14.0.0
+ # via chatlas
+setuptools==80.9.0 ; python_full_version >= '3.12'
+ # via shiny
+shiny==1.4.0
+ # via py-chat-app
+sniffio==1.3.1
+ # via
+ # anyio
+ # openai
+starlette==0.47.1
+ # via shiny
+tqdm==4.67.1
+ # via openai
+typing-extensions==4.14.0
+ # via
+ # anyio
+ # htmltools
+ # openai
+ # posit-sdk
+ # pydantic
+ # pydantic-core
+ # shiny
+ # starlette
+ # typing-inspection
+typing-inspection==0.4.1
+ # via pydantic
+uc-micro-py==1.0.3
+ # via linkify-it-py
+urllib3==2.5.0
+ # via requests
+uvicorn==0.35.0 ; sys_platform != 'emscripten'
+ # via shiny
+watchfiles==1.1.0 ; sys_platform != 'emscripten'
+ # via shiny
+wcwidth==0.2.13 ; sys_platform != 'emscripten'
+ # via prompt-toolkit
+websockets==15.0.1
+ # via shiny