|
| 1 | +--- |
| 2 | +title: "OAuthTokenResolver" |
| 3 | +id: oauthtokenresolver |
| 4 | +slug: "/oauthtokenresolver" |
| 5 | +description: "Resolves an OAuth access token at pipeline runtime and emits it for downstream components such as the SharePoint and Google Drive retrievers and fetchers." |
| 6 | +--- |
| 7 | + |
| 8 | +# OAuthTokenResolver |
| 9 | + |
| 10 | +Resolves an OAuth access token at pipeline runtime and emits it for downstream components such as the SharePoint and Google Drive retrievers and fetchers. |
| 11 | + |
| 12 | +<div className="key-value-table"> |
| 13 | + |
| 14 | +| | | |
| 15 | +| --- | --- | |
| 16 | +| **Most common position in a pipeline** | At the start of a pipeline, feeding `access_token` into downstream components such as [`MSSharePointRetriever`](../retrievers/mssharepointretriever.mdx) or [`GoogleDriveRetriever`](../retrievers/googledriveretriever.mdx) | |
| 17 | +| **Mandatory init variables** | `token_source`: The strategy that resolves the access token, for example `OAuthRefreshTokenSource` | |
| 18 | +| **Mandatory run variables** | None for config-only sources. `subject_token`: a controller-injected per-request credential, mandatory only when the source requires it (for example `OAuthTokenExchangeSource`) | |
| 19 | +| **Output variables** | `access_token`: A bearer token string | |
| 20 | +| **API reference** | [OAuth](/reference/integrations-oauth) | |
| 21 | +| **GitHub link** | https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/oauth | |
| 22 | +| **Package name** | `oauth-haystack` | |
| 23 | + |
| 24 | +</div> |
| 25 | + |
| 26 | +## Overview |
| 27 | + |
| 28 | +`OAuthTokenResolver` resolves an OAuth access token when the pipeline runs and emits it on the `access_token` output socket. Downstream components – such as [`MSSharePointRetriever`](../retrievers/mssharepointretriever.mdx), [`MSSharePointFetcher`](../fetchers/mssharepointfetcher.mdx), [`GoogleDriveRetriever`](../retrievers/googledriveretriever.mdx), and [`GoogleDriveFetcher`](../fetchers/googledrivefetcher.mdx) – consume the token through a normal connection and never need to know how it was obtained. |
| 29 | + |
| 30 | +The resolver itself is a thin wrapper. The actual work of getting a token is delegated to a pluggable **token source** that decides *where* the token comes from. This separation lets you swap authentication strategies (refresh-token grant, per-request token exchange, or a static long-lived token) without changing the rest of your pipeline. |
| 31 | + |
| 32 | +### Token sources |
| 33 | + |
| 34 | +You pass a token source to the resolver through the `token_source` parameter. All sources are importable from `haystack_integrations.utils.oauth`. |
| 35 | + |
| 36 | +| Source | Use it when | Per-request input | |
| 37 | +| --- | --- | --- | |
| 38 | +| `OAuthRefreshTokenSource` | You have a single, fixed identity backed by a stored refresh token and want the source to exchange it for short-lived access tokens and cache them. | None | |
| 39 | +| `OAuthTokenExchangeSource` | You serve multiple users (or run multiple replicas) and want to exchange an incoming per-request user assertion for a downstream token, with no persistent storage. Implements RFC 8693 token exchange and Microsoft's on-behalf-of flow. | `subject_token` | |
| 40 | +| `OAuthStaticTokenSource` | Your provider issues a non-expiring token that you manage out of band (for example Slack or Notion). | None | |
| 41 | + |
| 42 | +When the configured source needs a per-request credential (`OAuthTokenExchangeSource` sets `requires_subject_token = True`), the resolver declares a **mandatory** `subject_token` run input. This is a controller-injected credential – for example an incoming user assertion – not a value chosen by an end user. For config-only sources (`OAuthRefreshTokenSource`, `OAuthStaticTokenSource`), the resolver declares no run input and acts as a source node. |
| 43 | + |
| 44 | +:::info[Scopes are provider-specific] |
| 45 | + |
| 46 | +The OAuth scopes you request depend on the downstream service. For Microsoft Graph, that means scopes such as `https://graph.microsoft.com/Files.Read.All`; for Google Drive, scopes such as `https://www.googleapis.com/auth/drive.readonly`. Always consult your identity provider's documentation for the exact scope values. |
| 47 | + |
| 48 | +::: |
| 49 | + |
| 50 | +### Installation |
| 51 | + |
| 52 | +Install the OAuth integration with: |
| 53 | + |
| 54 | +```shell |
| 55 | +pip install oauth-haystack |
| 56 | +``` |
| 57 | + |
| 58 | +## Usage |
| 59 | + |
| 60 | +### On its own |
| 61 | + |
| 62 | +Resolve a token with a stored refresh token using `OAuthRefreshTokenSource`. The refresh token is read from an environment variable through the [Secret API](../../concepts/secret-management.mdx): |
| 63 | + |
| 64 | +```python |
| 65 | +from haystack.utils import Secret |
| 66 | +from haystack_integrations.components.connectors.oauth import OAuthTokenResolver |
| 67 | +from haystack_integrations.utils.oauth import OAuthRefreshTokenSource |
| 68 | + |
| 69 | +resolver = OAuthTokenResolver( |
| 70 | + token_source=OAuthRefreshTokenSource( |
| 71 | + token_url="https://login.microsoftonline.com/common/oauth2/v2.0/token", |
| 72 | + client_id="aaa-bbb-ccc", |
| 73 | + refresh_token=Secret.from_env_var("MS_REFRESH_TOKEN"), |
| 74 | + scopes=[ |
| 75 | + "https://graph.microsoft.com/Files.Read.All", |
| 76 | + "offline_access", |
| 77 | + ], |
| 78 | + ), |
| 79 | +) |
| 80 | + |
| 81 | +access_token = resolver.run()["access_token"] |
| 82 | +``` |
| 83 | + |
| 84 | +For a provider that issues long-lived, non-expiring tokens, use `OAuthStaticTokenSource` instead: |
| 85 | + |
| 86 | +```python |
| 87 | +from haystack.utils import Secret |
| 88 | +from haystack_integrations.components.connectors.oauth import OAuthTokenResolver |
| 89 | +from haystack_integrations.utils.oauth import OAuthStaticTokenSource |
| 90 | + |
| 91 | +resolver = OAuthTokenResolver( |
| 92 | + token_source=OAuthStaticTokenSource(token=Secret.from_env_var("SERVICE_TOKEN")), |
| 93 | +) |
| 94 | + |
| 95 | +access_token = resolver.run()["access_token"] |
| 96 | +``` |
| 97 | + |
| 98 | +For multi-user backends, use `OAuthTokenExchangeSource`. The resolver then requires a per-request `subject_token`: |
| 99 | + |
| 100 | +```python |
| 101 | +from haystack_integrations.components.connectors.oauth import OAuthTokenResolver |
| 102 | +from haystack_integrations.utils.oauth import OAuthTokenExchangeSource |
| 103 | + |
| 104 | +resolver = OAuthTokenResolver( |
| 105 | + token_source=OAuthTokenExchangeSource( |
| 106 | + token_url="https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token", |
| 107 | + client_id="aaa-bbb-ccc", |
| 108 | + subject_token_param="assertion", |
| 109 | + grant_type="urn:ietf:params:oauth:grant-type:jwt-bearer", |
| 110 | + scopes=["https://graph.microsoft.com/Files.Read.All"], |
| 111 | + extra_token_params={"requested_token_use": "on_behalf_of"}, |
| 112 | + ), |
| 113 | +) |
| 114 | + |
| 115 | +# `subject_token` is the incoming per-request user assertion, injected by your application. |
| 116 | +access_token = resolver.run(subject_token="<incoming-user-assertion>")["access_token"] |
| 117 | +``` |
| 118 | + |
| 119 | +### In a pipeline |
| 120 | + |
| 121 | +In a pipeline, connect the resolver's `access_token` output to the `access_token` input of one or more downstream components. The example below wires the resolver into a [`MSSharePointRetriever`](../retrievers/mssharepointretriever.mdx) so that searching SharePoint requires only a query at runtime: |
| 122 | + |
| 123 | +```python |
| 124 | +from haystack import Pipeline |
| 125 | +from haystack.utils import Secret |
| 126 | +from haystack_integrations.components.connectors.oauth import OAuthTokenResolver |
| 127 | +from haystack_integrations.utils.oauth import OAuthRefreshTokenSource |
| 128 | +from haystack_integrations.components.retrievers.microsoft_sharepoint import ( |
| 129 | + MSSharePointRetriever, |
| 130 | +) |
| 131 | + |
| 132 | +pipeline = Pipeline() |
| 133 | +pipeline.add_component( |
| 134 | + "resolver", |
| 135 | + OAuthTokenResolver( |
| 136 | + token_source=OAuthRefreshTokenSource( |
| 137 | + token_url="https://login.microsoftonline.com/common/oauth2/v2.0/token", |
| 138 | + client_id="aaa-bbb-ccc", |
| 139 | + refresh_token=Secret.from_env_var("MS_REFRESH_TOKEN"), |
| 140 | + scopes=[ |
| 141 | + "https://graph.microsoft.com/Files.Read.All", |
| 142 | + "https://graph.microsoft.com/Sites.Read.All", |
| 143 | + "offline_access", |
| 144 | + ], |
| 145 | + ), |
| 146 | + ), |
| 147 | +) |
| 148 | +pipeline.add_component("retriever", MSSharePointRetriever(top_k=5)) |
| 149 | +pipeline.connect("resolver.access_token", "retriever.access_token") |
| 150 | + |
| 151 | +result = pipeline.run({"retriever": {"query": "quarterly roadmap"}}) |
| 152 | +documents = result["retriever"]["documents"] |
| 153 | +``` |
| 154 | + |
| 155 | +A single `access_token` output can be connected to several downstream inputs. For a full retrieve-then-fetch pipeline that feeds the same token to both a retriever and a fetcher, see the [`MSSharePointFetcher`](../fetchers/mssharepointfetcher.mdx) and [`GoogleDriveFetcher`](../fetchers/googledrivefetcher.mdx) pages. |
0 commit comments