<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Okta Developer</title>
    <description>Secure, scalable, and highly available authentication and user management for any app.
</description>
    <link>https://developer.okta.com</link>
    <atom:link href="https://developer.okta.com/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Build a Flask App with Okta for Secure OIDC Login and Authorized API Calls</title>
        <description>&lt;p&gt;Python syntax and the flexibility of the Flask microframework make it a popular choice for quickly building web applications. While Flask provides the essentials to get you started, you’ll need to tackle two critical pieces yourself: secure user authentication and authorized access to your backend services. After all, how do you securely sign users into your application? And once they’re signed in, how does your app fetch data from a backend service that only serves authorized requests?&lt;/p&gt;

&lt;p&gt;This tutorial shows you how to solve both. You’ll build a Flask dashboard app that signs users in with Okta using OpenID Connect (OIDC). Once signed in, the app uses the resulting OAuth 2.0 access token to call a separate backend API that only responds to authorized requests, a common pattern where a web app needs to fetch data from a protected backend service. You’ll use Authlib, an OIDC client library, to configure the Authorization Code flow with PKCE automatically. You’ll also learn how to add a custom scope to the access token and validate it on the backend to control what data the API returns.&lt;/p&gt;

&lt;p&gt;Check out the complete source code on &lt;a href=&quot;https://github.com/oktadev/okta-flask-oauth-example&quot;&gt;GitHub&lt;/a&gt; and get started without setting it up from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#build-a-flask-app-with-oidc-authentication&quot; id=&quot;markdown-toc-build-a-flask-app-with-oidc-authentication&quot;&gt;Build a Flask app with OIDC authentication&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#create-an-app-integration-in-the-okta-admin-console&quot; id=&quot;markdown-toc-create-an-app-integration-in-the-okta-admin-console&quot;&gt;Create an app integration in the Okta Admin Console&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#enable-self-service-user-registration&quot; id=&quot;markdown-toc-enable-self-service-user-registration&quot;&gt;Enable self-service user registration&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#add-secure-user-login-to-a-flask-application-with-okta&quot; id=&quot;markdown-toc-add-secure-user-login-to-a-flask-application-with-okta&quot;&gt;Add secure user login to a Flask application with Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-basic-flask-web-application&quot; id=&quot;markdown-toc-create-a-basic-flask-web-application&quot;&gt;Create a basic Flask web application&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#configure-flask-environment-variables&quot; id=&quot;markdown-toc-configure-flask-environment-variables&quot;&gt;Configure Flask environment variables&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#implement-the-oidc-authentication-logic&quot; id=&quot;markdown-toc-implement-the-oidc-authentication-logic&quot;&gt;Implement the OIDC authentication logic&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#run-the-flask-oauth-web-app&quot; id=&quot;markdown-toc-run-the-flask-oauth-web-app&quot;&gt;Run the Flask OAuth web app&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#call-a-protected-api-with-oauth-scoped-tokens&quot; id=&quot;markdown-toc-call-a-protected-api-with-oauth-scoped-tokens&quot;&gt;Call a protected API with OAuth scoped tokens&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#build-a-protected-users-api&quot; id=&quot;markdown-toc-build-a-protected-users-api&quot;&gt;Build a protected users API&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#extend-the-flask-app-to-interact-with-the-resource-server&quot; id=&quot;markdown-toc-extend-the-flask-app-to-interact-with-the-resource-server&quot;&gt;Extend the Flask app to interact with the resource server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#update-the-flask-ui-to-display-protected-data&quot; id=&quot;markdown-toc-update-the-flask-ui-to-display-protected-data&quot;&gt;Update the Flask UI to display protected data&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#test-the-protected-api-integration&quot; id=&quot;markdown-toc-test-the-protected-api-integration&quot;&gt;Test the protected API integration&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-oauth-and-oidc&quot; id=&quot;markdown-toc-learn-more-about-oauth-and-oidc&quot;&gt;Learn more about OAuth and OIDC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;build-a-flask-app-with-oidc-authentication&quot;&gt;Build a Flask app with OIDC authentication&lt;/h2&gt;

&lt;p&gt;In this tutorial, you’ll build a simple dashboard application and learn how to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Securely sign users in to view their profile information using Okta as the OpenID Connect identity provider.&lt;/li&gt;
  &lt;li&gt;Use Authlib to configure the Authorization Code flow with &lt;a href=&quot;https://oauth.net/2/pkce/&quot;&gt;PKCE&lt;/a&gt; automatically, following OAuth 2.1 security best practices.&lt;/li&gt;
  &lt;li&gt;Add a feature for new users to self-register for an account directly from the sign-in page.&lt;/li&gt;
  &lt;li&gt;Enable authenticated users to interact with a protected API using access tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.python.org/&quot;&gt;Python&lt;/a&gt; 3.14 or later and &lt;a href=&quot;https://pypi.org/project/pip/&quot;&gt;pip&lt;/a&gt; installed&lt;/li&gt;
  &lt;li&gt;An &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Okta Integrator Free Plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;create-an-app-integration-in-the-okta-admin-console&quot;&gt;Create an app integration in the Okta Admin Console&lt;/h2&gt;

&lt;p&gt;Before you begin, you’ll need an Okta Integrator Free Plan account. To get one, sign up for an &lt;a href=&quot;https://developer.okta.com/login&quot;&gt;Integrator account&lt;/a&gt;. Once you have an account, sign in to your &lt;a href=&quot;https://developer.okta.com/login&quot;&gt;Integrator account&lt;/a&gt;. Next, in the Admin Console:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Go to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Create App Integration&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;OIDC - OpenID Connect&lt;/strong&gt; as the sign-in method&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Web Application&lt;/strong&gt; as the application type, then click &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Enter an app integration name&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Configure the redirect URIs:
    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5000/authorization-code/callback&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Sign-out redirect URIs&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5000&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In the &lt;strong&gt;Controlled access&lt;/strong&gt; section, select the appropriate access level&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;details&gt;
  &lt;summary&gt;Where are my new app's credentials?&lt;/summary&gt;

&lt;p&gt;Creating an OIDC Web App manually in the Admin Console configures your Okta Org with the application settings.&lt;/p&gt;

&lt;p&gt;After creating the app, you can find the configuration details on the app’s &lt;strong&gt;General&lt;/strong&gt; tab:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Client ID&lt;/strong&gt;: Found in the &lt;strong&gt;Client Credentials&lt;/strong&gt; section&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Client Secret&lt;/strong&gt;: Click &lt;strong&gt;Show&lt;/strong&gt; in the &lt;strong&gt;Client Credentials&lt;/strong&gt; section to reveal&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Issuer&lt;/strong&gt;: Found in the &lt;strong&gt;Issuer URI&lt;/strong&gt; field for the authorization server that appears by selecting &lt;strong&gt;Security&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt; from the navigation pane.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll need these values for your application configuration:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;OKTA_OAUTH2_ISSUER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://dev-133337.okta.com/oauth2/default&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_OAUTH2_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0oab8eb55Kb9jdMIr5d6&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_OAUTH2_CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;NEVER-SHOW-SECRETS&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your Okta domain is the first part of your issuer, before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth2/default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can also use the &lt;a href=&quot;https://github.com/okta/okta-cli-client&quot;&gt;Okta CLI Client&lt;/a&gt; or &lt;a href=&quot;https://github.com/okta/okta-powershell-cli&quot;&gt;Okta PowerShell Module&lt;/a&gt; to automate this process. See &lt;a href=&quot;https://developer.okta.com/docs/guides/sign-into-web-app/-/create-okta-application/&quot;&gt;this guide&lt;/a&gt; for more information about setting up your app.&lt;/p&gt;

&lt;/details&gt;

&lt;h2 id=&quot;enable-self-service-user-registration&quot;&gt;Enable self-service user registration&lt;/h2&gt;

&lt;p&gt;User registration is vital for any application, and Okta makes the process quick and hassle-free. See &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/about-ssr.htm&quot;&gt;Self-Service Registration&lt;/a&gt; for an overview of how it works. Setting this up involves two main steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a user profile policy - This policy defines the attributes a user must provide when self-registering. See the &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/create-profile-enrollment-policy.htm&quot;&gt;Okta documentation&lt;/a&gt; for detailed instructions.&lt;/li&gt;
  &lt;li&gt;Assign your application to the policy - This step is required; without it, the self-service registration flow will not activate for your app. See the &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/select-profile-enrollment-policy.htm&quot;&gt;product documentation&lt;/a&gt; for more details.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;add-secure-user-login-to-a-flask-application-with-okta&quot;&gt;Add secure user login to a Flask application with Okta&lt;/h2&gt;

&lt;p&gt;Now that you’ve configured your app integration in Okta and noted the required Okta application details, it’s time to create your Flask application.&lt;/p&gt;

&lt;h3 id=&quot;create-a-basic-flask-web-application&quot;&gt;Create a basic Flask web application&lt;/h3&gt;

&lt;p&gt;First, you’ll create a project folder and a Python virtual environment, then install the necessary dependencies.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a project folder named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask-web-app&lt;/code&gt; and a subfolder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;venv&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Open a terminal window in the project folder and create a Python virtual environment&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; venv venv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Activate the virtual environment. Use the command that corresponds to your operating system.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# On macOS/Linux&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;source &lt;/span&gt;venv/bin/activate

&lt;span class=&quot;c&quot;&gt;# On Windows&lt;/span&gt;
venv&lt;span class=&quot;se&quot;&gt;\S&lt;/span&gt;cripts&lt;span class=&quot;se&quot;&gt;\a&lt;/span&gt;ctivate.bat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;With your environment active, install the required libraries:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Authlib&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;1.6.1 Flask[async]&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;3.1.1 python-dotenv&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;1.1.1 &lt;span class=&quot;nv&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;2.32.4 okta-jwt-verifier&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt;0.3.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;configure-flask-environment-variables&quot;&gt;Configure Flask environment variables&lt;/h3&gt;

&lt;p&gt;To keep sensitive credentials out of source control, you’ll store them in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; file and load them into the application’s configuration. This practice prevents hard-coding secrets directly into your source code. Make sure to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt; file to prevent accidentally committing your credentials to source control.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; in the project root and add your Okta application’s configuration details. Replace the placeholders with your actual values.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;OKTA_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;yourClientId&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;yourClientSecret&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_BASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;yourOktaDomain&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.py&lt;/code&gt; file in the project root. This file reads the values from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; and constructs the OIDC metadata URL, which the application uses to fetch endpoints from Okta.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;OKTA_CLIENT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_ID'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_CLIENT_SECRET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_SECRET'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OKTA_BASE_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_METADATA_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OKTA_BASE_URL&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/.well-known/openid-configuration&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;implement-the-oidc-authentication-logic&quot;&gt;Implement the OIDC authentication logic&lt;/h3&gt;

&lt;p&gt;Next, create the main application file, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt;, in your project’s root directory. This file holds the core logic for handling OIDC authentication. Paste the following code into it to configure the basic application.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;authlib.integrations.flask_client&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OAuth&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;dotenv&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_dotenv&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;load_dotenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;secret_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;urandom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'config'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;initialize-the-okta-oidc-client&quot;&gt;Initialize the Okta OIDC client&lt;/h4&gt;

&lt;p&gt;To set up the connection to Okta, add the following code to the bottom of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; file. This code initializes an OAuth object and registers the Okta application as a remote provider.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Create a registry with an OAuth object
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Register a remote application
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'okta'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_ID'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_SECRET'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;server_metadata_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_METADATA_URL'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client_kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'scope'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'openid profile email offline_access'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'code_challenge_method'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'S256'&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first parameter in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;register()&lt;/code&gt; method (in this case, &lt;em&gt;Okta&lt;/em&gt;) is the name of the remote application. You’ll later access the remote application with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oauth.okta&lt;/code&gt; to handle all the OIDC-related functions.&lt;/p&gt;

&lt;p&gt;The client_kwargs dictionary passes extra parameters to the authorization request. Because the code_challenge_method is present, the Authlib library automatically uses the &lt;a href=&quot;https://oauth.net/2/pkce/&quot;&gt;PKCE&lt;/a&gt; flow, which enhances security.&lt;/p&gt;

&lt;h4 id=&quot;build-flask-routes-for-oidc-authentication&quot;&gt;Build Flask routes for OIDC authentication&lt;/h4&gt;

&lt;p&gt;The sign-in process begins when the user clicks the Login button, which redirects the browser to the Okta-hosted Sign-In page.&lt;/p&gt;

&lt;p&gt;After the user signs in, Okta redirects the browser to the configured sign-in redirect URI with an authorization code. Authlib then exchanges that code for tokens and automatically calls Okta’s userinfo endpoint to retrieve the user’s profile. Similarly, after a user signs out, Okta redirects the browser to the sign-out redirect URI.&lt;/p&gt;

&lt;p&gt;Let’s start by creating a simple HTML page that allows the user to log in. This page will display a sign-in button for signed-out users and a sign-out button for logged-in users, along with profile information.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/login&lt;/code&gt; route to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; file. This endpoint serves as the entry point for the authentication flow. It constructs the authorization redirect URI and sends the user’s browser to the Okta-hosted Sign-In page to begin the OIDC process.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/login'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redirect_uri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'authorization_code_callback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_external&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;okta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authorize_redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;redirect_uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Next, implement the callback handler for the Redirect URI you configured in your Okta application (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5000/authorization-code/callback&lt;/code&gt;). This handler processes the authorization code returned by Okta. Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authorize_access_token()&lt;/code&gt; triggers Authlib to exchange the code for tokens and automatically call Okta’s userinfo endpoint. Both the userinfo and the full token are then stored in Flask’s session for use across requests.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/authorization-code/callback'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;authorization_code_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;okta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authorize_access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'loggedInUser'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'userinfo'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/logout&lt;/code&gt; route. This endpoint clears the local user session, signing the user out of the Flask application. It then constructs a redirect URL to Okta’s logout_endpoint, which terminates the user’s session with Okta, ensuring a complete and secure sign-out.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/logout'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;logout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;id_token_hint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;id_token_hint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'id_token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;okta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_metadata&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;logout_endpoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'end_session_endpoint'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'loggedInUser'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logout_endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;logout_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logout_endpoint&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;?'&lt;/span&gt;
            &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'id_token_hint=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id_token_hint&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;'&lt;/span&gt;
            &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'post_logout_redirect_uri=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;index&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_external&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logout_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Finally, update the root route (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;). This route now checks the session for a signed-in user and passes that information to the template.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'loggedInUser'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the back-end routes in place, you can now create the HTML template files to provide a user interface for signing in, signing out, and displaying profile data.&lt;/p&gt;

&lt;p&gt;Step 1: Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;templates&lt;/code&gt; directory in your project root. Inside it, create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file. This page will display a Login button for signed-out users and, for signed-in users, profile information with a Logout button.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Home&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ url_for('static', filename='css/index.css') }}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wrapper&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;nav&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;main-nav&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;navigation&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Primary navigation&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-left&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Flask Web App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          {% if user %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/fetch-users&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Fetch Users&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/logout&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          {% else %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/login&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Login&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          {% endif %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;nt&quot;&gt;&amp;lt;main&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        {% if user %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User welcome&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user-info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome, {{ user.name or user.preferred_username or 'User' }}!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;You have successfully logged in. You can now fetch and view user details.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Click the button below to load the list of users.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;location.href='/fetch-users'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Fetch Users&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

        {% else %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Login prompt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user-info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to the Flask Web App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you are seeing this, you are not logged in yet.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This app uses OpenID Connect (OIDC) for secure login. After logging in, you will be able to access user-specific data.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Click the login button below to get started.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/login&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button-link&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Login / Register&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        {% endif %}
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Step 2: For styling, create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/css&lt;/code&gt; directory in your project root. Inside it, create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.css&lt;/code&gt; and add the following code.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&quot;Segoe UI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Tahoma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Geneva&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Verdana&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;nav&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.main-nav&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#2f2f2f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f0f0f0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;space-between&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;nav&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.main-nav&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#f0f0f0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.5rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;nav&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.main-nav&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#bfbfbf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;underline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.wrapper&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100vh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;section&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.user-info&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;transparent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.5rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#222222&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.button-link&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#1a73e8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.6rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1.3rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;cursor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;text-decoration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nt&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;.button-link&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:hover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#155ab6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;run-the-flask-oauth-web-app&quot;&gt;Run the Flask OAuth web app&lt;/h3&gt;

&lt;p&gt;You’re ready to test the sign-in and sign-out functionality.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Start the application with the Flask server.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flask run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Open a new terminal window and start the resource server.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flask &lt;span class=&quot;nt&quot;&gt;--app&lt;/span&gt; resource-server.py run &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 5001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Open a browser and go to http://localhost:5000.&lt;/li&gt;
  &lt;li&gt;Click Login. The browser redirects you to the Okta Sign-In Widget.&lt;/li&gt;
  &lt;li&gt;After you sign in, Okta redirects you back to your application, and the page displays the signed-in user’s details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/flask-oauth-web-app/flask-app-after-login-a26fa84d54cf98534da58634e2acf7c22ef6d282e055c102cfe03a0f45bd549e.jpg&quot; alt=&quot;Flask web app home page showing a welcome message after successful login with a Fetch Users button&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click Logout. The browser clears your session and returns you to the home page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ve successfully added a secure authentication flow to your Flask app. Next, you’ll extend it to call a protected API using scoped tokens.&lt;/p&gt;

&lt;h2 id=&quot;call-a-protected-api-with-oauth-scoped-tokens&quot;&gt;Call a protected API with OAuth scoped tokens&lt;/h2&gt;

&lt;p&gt;Using OAuth for API authorization is a modern security best practice. It allows you to use access tokens to interact with APIs to fetch and manage data securely. This section walks you through how to use a scoped access token to fetch data from your own resource server.&lt;/p&gt;

&lt;p&gt;Every action on an endpoint that supports OAuth 2.0 requires a specific scope. In this example, your endpoint requires the specific scope &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:read-users&lt;/code&gt; to return a list of sample users.&lt;/p&gt;

&lt;p&gt;To include this scope in the tokens minted from Okta, follow these steps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In your Okta Admin Console, navigate to &lt;strong&gt;Security&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Select your &lt;strong&gt;Authorization Server&lt;/strong&gt; and go to the &lt;strong&gt;Scopes&lt;/strong&gt; tab&lt;/li&gt;
  &lt;li&gt;Click the &lt;strong&gt;Add Scope&lt;/strong&gt; button and enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:read-users&lt;/code&gt; in the &lt;strong&gt;Name&lt;/strong&gt; field.&lt;/li&gt;
  &lt;li&gt;Optionally, enter the &lt;strong&gt;Description&lt;/strong&gt; and the &lt;strong&gt;Display Phrase&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, when this scope is requested, Okta will include it in the tokens it issues.&lt;/p&gt;

&lt;p&gt;Before creating the resource server, add an extra layer of security. You want your endpoint only to consume tokens intended for your server, preventing tokens issued for other services from being replayed or misused.&lt;/p&gt;

&lt;p&gt;To do this, restrict the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; claim that Okta returns in the access token to your resource server. To achieve this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In your Okta Admin Console, navigate to &lt;strong&gt;Security&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Select your &lt;strong&gt;Authorization Server&lt;/strong&gt;. In the &lt;strong&gt;Settings&lt;/strong&gt; tab, click &lt;strong&gt;Edit&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Change the &lt;strong&gt;Audience&lt;/strong&gt; field to &lt;a href=&quot;http://localhost:5001&quot;&gt;http://localhost:5001&lt;/a&gt; — this is where your resource server will be hosted&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;build-a-protected-users-api&quot;&gt;Build a protected users API&lt;/h3&gt;

&lt;p&gt;For this demo app, you’ll create a minimal resource server with a single route called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch-users&lt;/code&gt; that returns a list of sample users.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Start by modifying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; file. Add the variable that stores the domain of your resource server (in this case, http://localhost:5001):&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;OKTA_CLIENT_ID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;yourClientId&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_CLIENT_SECRET&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;={&lt;/span&gt;yourClientSecret&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;OKTA_BASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;yourOktaDomain&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RESOURCE_SERVER_BASE_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;http://localhost:5001
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now, modify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.py&lt;/code&gt; file to include the resource server URL and the list of sample users. Replace the existing code with the following:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;OKTA_CLIENT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_ID'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_CLIENT_SECRET&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_SECRET'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;OKTA_BASE_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OKTA_METADATA_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OKTA_BASE_URL&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/.well-known/openid-configuration&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;RESOURCE_SERVER_BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;RESOURCE_SERVER_BASE_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;SAMPLE_USERS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Alice Johnson&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Alice&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Johnson&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;alice.johnson@example.com&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bob Smith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Bob&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Smith&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bob.smith@example.com&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Charlie Brown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;firstName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Charlie&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;lastName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Brown&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;charlie.brown@example.com&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource-server.py&lt;/code&gt; file in the project directory and add the following code:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;okta_jwt_verifier&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseJWTVerifier&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'config'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;ISSUER&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_BASE_URL'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;CLIENT_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'OKTA_CLIENT_ID'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;AUDIENCE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'RESOURCE_SERVER_BASE_URL'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;REQUIRED_SCOPES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;api:read-users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/fetch-users'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_valid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error_response&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'SAMPLE_USERS'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;The endpoint returns sample users if the token is valid. Next, you’ll add token validation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now write the logic for token validation. You’ll validate the token’s issuer, audience, and client ID against your authorization server URL, resource server domain, and client ID. You’ll also verify that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:read-users&lt;/code&gt; scope is present in the token’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; claim.&lt;/p&gt;

    &lt;p&gt;Start with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify_token&lt;/code&gt; function. First, check if the Authorization header is of type Bearer:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;No token provided&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bearer&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Authorization header must be Bearer token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Next, use Okta’s package to verify that the audience, issuer, and client ID in the token match your configuration:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseJWTVerifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISSUER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CLIENT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AUDIENCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verify_access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token verification failed: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Invalid or Expired Token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Now add scope validation. Extract the claims from the token and check that the custom scope &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:read-users&lt;/code&gt; is present in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; claim. If the token doesn’t contain this scope, reject it and return an error. In the try block, before the return statement, add the following code:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;token_scopes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;scp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REQUIRED_SCOPES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issubset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Insufficient Scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token is Valid!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Once everything is done, your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify_token&lt;/code&gt; function should look like this:&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;verify_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;No token provided&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;auth_header&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;bearer&quot;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Authorization header must be Bearer token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseJWTVerifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ISSUER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CLIENT_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AUDIENCE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verify_access_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwt_verifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;token_scopes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;claims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;scp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;REQUIRED_SCOPES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issubset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token_scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Insufficient Scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token is Valid!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Token verification failed: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;error&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Invalid or Expired Token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;401&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;extend-the-flask-app-to-interact-with-the-resource-server&quot;&gt;Extend the Flask app to interact with the resource server&lt;/h3&gt;

&lt;p&gt;With the configuration in Okta complete, the next step is to update your Flask application to request this scope. This ensures that the access token issued to the user includes the necessary permission to call the protected endpoint.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; file, find the oauth.register block and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:read-users&lt;/code&gt; to the scope string within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_kwargs&lt;/code&gt; dictionary.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# ... other parameters
&lt;/span&gt;     &lt;span class=&quot;n&quot;&gt;client_kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;'scope'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'openid profile email offline_access api:read-users'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;'code_challenge_method'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'S256'&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add a new route, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/fetch-users&lt;/code&gt;, to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; file. This endpoint uses the stored access token from the user’s session to make a secure GET request to the protected endpoint in the resource server. If successful, it passes the returned list of users to the index.html template for display.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/fetch-users'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fetchUsers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'loggedInUser'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'login'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;access_token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'access_token'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       
    &lt;span class=&quot;n&quot;&gt;RESOURCE_SERVER_BASE_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'RESOURCE_SERVER_BASE_URL'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Bearer &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;access_token&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESOURCE_SERVER_BASE_URL&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/fetch-users&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;API request failed: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status_code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Update the root route (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;) to initialize the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userList&lt;/code&gt; variable in the template. This prevents errors when the page first loads before any data is fetched. Replace the existing index function with the following code.&lt;/p&gt;

    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'loggedInUser'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loggedInUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;userList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;update-the-flask-ui-to-display-protected-data&quot;&gt;Update the Flask UI to display protected data&lt;/h3&gt;

&lt;p&gt;Now, let’s modify the front end to add a button that triggers the API call and a table to display the returned user data.&lt;/p&gt;

&lt;p&gt;Step 1: Open the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;templates/index.html&lt;/code&gt; file and replace its entire contents with the updated code below. This version adds a Fetch Users button for signed-in users and a table to display the user list when available.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;lang=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;charset=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UTF-8&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Home&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ url_for('static', filename='css/index.css') }}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wrapper&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;nav&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;main-nav&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;navigation&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Primary navigation&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-left&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Flask Web App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nav-right&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          {% if user %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/fetch-users&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Fetch Users&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/logout&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          {% else %}
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/login&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Login&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
          {% endif %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;

      &lt;span class=&quot;nt&quot;&gt;&amp;lt;main&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;container&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        {% if user %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;User welcome&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user-info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome, {{ user.name or 'User' }}!&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;You have successfully logged in. You can now fetch and view user details.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Click the button below to load the list of users.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;button&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onclick=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;location.href='/fetch-users'&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Fetch Users&lt;span class=&quot;nt&quot;&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;

        {% if userList %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Users table&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Users List&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;table-wrapper&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;nt&quot;&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Email&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;First Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                  &lt;span class=&quot;nt&quot;&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Last Name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;nt&quot;&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;table-body-scroll&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;nt&quot;&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
                  {% for user in userList %}
                  &lt;span class=&quot;nt&quot;&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ user.email }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ user.firstName }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;td&amp;gt;&lt;/span&gt;{{ user.lastName }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
                  {% endfor %}
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
              &lt;span class=&quot;nt&quot;&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        {% endif %} {% else %}
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;section&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;aria-label=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Login prompt&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user-info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to the Flask Web App&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If you are seeing this, you are not logged in yet.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This app uses OpenID Connect (OIDC) for secure login. After logging in, you will be able to access user-specific data.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Click the login button below to get started.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;a&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/login&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;button-link&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Login / Register&lt;span class=&quot;nt&quot;&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        {% endif %}
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Step 2: To style the new user table, open the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/css/index.css&lt;/code&gt; file and add the following CSS to the end of the file.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.table-body-scroll&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;overflow-y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#d1d9e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;6px&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
  &lt;span class=&quot;nt&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;collapse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#fff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#2c3e50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8px&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;table-layout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;fixed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
  &lt;span class=&quot;nt&quot;&gt;thead&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;th&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e9f0fb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#1a3c72&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;700&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.75rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#d1d9e6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sticky&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
  &lt;span class=&quot;nt&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;center&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0.75rem&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;border-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1px&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;solid&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;#e2e8f0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1rem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;word-wrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;break-word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;test-the-protected-api-integration&quot;&gt;Test the protected API integration&lt;/h3&gt;

&lt;p&gt;Ready to see it all work? Make sure both servers are running: the Flask app (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask run&lt;/code&gt;) and the resource server (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flask --app resource-server.py run --port 5001&lt;/code&gt;). Open a browser, go to http://localhost:5000, sign in, and then click Fetch Users. A table should appear with a list of sample users returned from your resource server. If you see the user data, your integration is a success!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/flask-oauth-web-app/flask-app-fetch-users-0b741e1ec23cdec3b76dbecb382a4929dfc89cd539df1bc37b1d0acacb0fecd1.jpg&quot; alt=&quot;Flask web app displaying a users table with email, first name, and last name columns after successfully fetching data from the protected API&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Your Flask application now has secure user authentication and can use an access token to interact with a protected API.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-oauth-and-oidc&quot;&gt;Learn more about OAuth and OIDC&lt;/h2&gt;

&lt;p&gt;If you’d like to learn more about the concepts covered in this tutorial, explore these official Okta resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/docs/guides/sign-into-web-app-redirect/python/main/&quot;&gt;Sign users in to your web app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/docs/guides/implement-grant-type/authcodepkce/main/&quot;&gt;Authorization Code flow with PKCE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/docs/guides/validate-access-tokens/python/main/&quot;&gt;Validate Access Tokens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/docs/guides/customize-authz-server/main/&quot;&gt;Create an Authorization Server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2025/07/28/express-oauth-pkce&quot;&gt;Secure a Node Express App with OAuth 2.0 and PKCE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube channel&lt;/a&gt; for more exciting content. We also want to hear from you about the topics you’d like to see and any questions you may have. Leave us a comment below!&lt;/p&gt;

</description>
        <pubDate>Tue, 28 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/28/flask-oauth-web-app</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/28/flask-oauth-web-app</guid>
      </item>
    
      <item>
        <title>Enable Your SAML Requesting App for Cross App Access</title>
        <description>&lt;p&gt;If you currently federate enterprise customers using Security Assertion Markup Language (SAML) and want to connect with third-party applications without migrating to OpenID Connect (OIDC), this Cross App Access (XAA) guide is for you.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/&quot;&gt;Identity Assertion Authorization Grant specification&lt;/a&gt;, the basis of XAA, was originally designed with OIDC in mind. To use it in SAML applications, you must accommodate specific security and uniqueness requirements. This guide details what you need to support and how to make resource requests to a third-party app using XAA.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#how-xaa-in-saml-works&quot; id=&quot;markdown-toc-how-xaa-in-saml-works&quot;&gt;How XAA in SAML works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#xaa-implementation-checklist-for-saml-federated-applications&quot; id=&quot;markdown-toc-xaa-implementation-checklist-for-saml-federated-applications&quot;&gt;XAA implementation checklist for SAML-federated applications&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#request-the-refresh-token&quot; id=&quot;markdown-toc-request-the-refresh-token&quot;&gt;Request the refresh token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#request-the-id-jag-token&quot; id=&quot;markdown-toc-request-the-id-jag-token&quot;&gt;Request the ID-JAG token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#request-the-access-token&quot; id=&quot;markdown-toc-request-the-access-token&quot;&gt;Request the access token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#handle-token-expiration&quot; id=&quot;markdown-toc-handle-token-expiration&quot;&gt;Handle token expiration&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#making-cross-application-requests-from-your-saml-app-securely&quot; id=&quot;markdown-toc-making-cross-application-requests-from-your-saml-app-securely&quot;&gt;Making cross-application requests from your SAML app securely&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configure-your-xaa-saml-requesting-app-in-okta&quot; id=&quot;markdown-toc-configure-your-xaa-saml-requesting-app-in-okta&quot;&gt;Configure your XAA SAML Requesting App in Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#register-and-configure-the-ai-agent-in-okta&quot; id=&quot;markdown-toc-register-and-configure-the-ai-agent-in-okta&quot;&gt;Register and configure the AI Agent in Okta&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#test-the-saml-20-app&quot; id=&quot;markdown-toc-test-the-saml-20-app&quot;&gt;Test the SAML 2.0 app&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#validate-the-xaa-connection-end-to-end&quot; id=&quot;markdown-toc-validate-the-xaa-connection-end-to-end&quot;&gt;Validate the XAA connection end-to-end&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-cross-app-access-saml-and-oauth-20&quot; id=&quot;markdown-toc-learn-more-about-cross-app-access-saml-and-oauth-20&quot;&gt;Learn more about Cross App Access, SAML, and OAuth 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-xaa-in-saml-works&quot;&gt;How XAA in SAML works&lt;/h2&gt;

&lt;p&gt;When an agent (like one running in Claude) needs API access, it presents an &lt;strong&gt;Identity Assertion Authorization Grant (ID-JAG)&lt;/strong&gt;. The ID-JAG is a short-lived JSON Web Token (JWT) issued by the Identity Provider (IdP) for your app’s user. You exchange the ID-JAG token for an access token to the resource application you’re connecting with.&lt;/p&gt;

&lt;p&gt;The sequence diagram shown below describes the SAML XAA flow and how your application fits in. You’ll handle the flow in two parts: where your application requests the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID-JAG&lt;/code&gt; from the IdP using a refresh token, and where your app requests the access token from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID-JAG&lt;/code&gt; from the third-party resource app’s authorization server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-saml-requester/xaa-saml-sequence-diagram-6bc6e11c736b50ad2fb90bae87c966218b33dca67112dc408a9a1faacf299967.svg&quot; alt=&quot;Sequence diagram showing SAML SSO between the user and Okta IdP, two OAuth token exchanges producing a refresh token and then an ID-JAG, and then requests an access token from the ID-JAG to call the API.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;xaa-implementation-checklist-for-saml-federated-applications&quot;&gt;XAA implementation checklist for SAML-federated applications&lt;/h2&gt;

&lt;p&gt;Follow the guide in this section to support XAA in your SAML application when your app connects to a third-party resource application. The XAA flow places the burden of token validation onto the IdP and the resource app’s authorization server. The initial SSO step remains the same; however, you’ll add three requests before getting an access token to make the resource API call.&lt;/p&gt;

&lt;p&gt;Once the user completes signing in, you’ll:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Request an OAuth refresh token from Okta&lt;/li&gt;
  &lt;li&gt;Request the ID-JAG token from Okta&lt;/li&gt;
  &lt;li&gt;Request the OAuth access token from the third-party resource app’s OAuth authorization server&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;request-the-refresh-token&quot;&gt;Request the refresh token&lt;/h3&gt;

&lt;p&gt;Your SAML application makes the initial SSO handshake and handles the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SAMLResponse&lt;/code&gt; as usual. The ACS handler verifies the incoming payload and its digital signature, then extracts the underlying XML assertion. This assertion serves as the required credential for the subsequent exchange.&lt;/p&gt;

&lt;p&gt;Immediately after validating the SAML response, in the same ACS request before redirecting the user onward, you’ll perform the first exchange. Base64-encode the assertion you extracted, then exchange it for a refresh token using Okta’s token endpoint. This exchange follows the &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8693&quot;&gt;OAuth 2.0 Token Exchange (RFC 8693)&lt;/a&gt; mechanism.&lt;/p&gt;

&lt;p&gt;This step requires a credential in the request. Because you’re making a sensitive resource request, Okta requires a JWT signed with an asymmetric key pair as the credential – a private key JWT.&lt;/p&gt;

&lt;p&gt;Follow the instructions in the &lt;a href=&quot;https://developer.okta.com/docs/guides/build-self-signed-jwt/js/main/&quot;&gt;Build a JWT for Client Authentication&lt;/a&gt; for your tech stack. The signed JWT becomes a client assertion in the request.&lt;/p&gt;

&lt;p&gt;The refresh token HTTP request looks like this:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/oauth2/v1/token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;your-okta-domain.okta.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&amp;amp;
subject_token=&amp;lt;base64url-encoded SAML assertion XML&amp;gt;&amp;amp;
subject_token_type=urn:ietf:params:oauth:token-type:saml2&amp;amp;
requested_token_type=urn:ietf:params:oauth:token-type:refresh_token&amp;amp;
scope=openid+offline_access&amp;amp;
client_id=&amp;lt;your_client_id&amp;gt;&amp;amp;
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&amp;amp;
client_assertion=&amp;lt;your signed JWT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The IdP (Okta) responds with the refresh token. The refresh token is opaque and long-lived. Persist the refresh token for the session; never persist or use the SAML assertion past this exchange.&lt;/p&gt;

&lt;h3 id=&quot;request-the-id-jag-token&quot;&gt;Request the ID-JAG token&lt;/h3&gt;

&lt;p&gt;With the refresh token in hand, you have the credentials to request the ID-JAG token when your application needs resources from a third-party app. This exchange uses the same OAuth token exchange mechanism as the first step. You make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; request to the same endpoint, Okta IdP, using the refresh token, and request the ID-JAG token type.&lt;/p&gt;

&lt;p&gt;This request also requires a signed JWT as it’s requesting a sensitive resource.&lt;/p&gt;

&lt;p&gt;The HTTP request looks like this:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/oauth2/v1/token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;your-okta-domain.okta.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;

grant_type=urn:ietf:params:oauth:grant-type:token-exchange&amp;amp;
subject_token=&amp;lt;the refresh_token from prior step&amp;gt;&amp;amp;
subject_token_type=urn:ietf:params:oauth:token-type:refresh_token&amp;amp;
requested_token_type=urn:ietf:params:oauth:token-type:id-jag&amp;amp;
audience=&amp;lt;the resource app's authorization server issuer URI&amp;gt;&amp;amp;
resource=&amp;lt;the resource app's API base URL&amp;gt;&amp;amp;
scope=&amp;lt;the resource app's required scopes&amp;gt;&amp;amp;
client_id=&amp;lt;your_client_id&amp;gt;&amp;amp;
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&amp;amp;
client_assertion=&amp;lt;your signed JWT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The IdP responds with a short-lived, signed JSON Web Token (JWT), the ID-JAG, as the value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; property within the payload.&lt;/p&gt;

&lt;h3 id=&quot;request-the-access-token&quot;&gt;Request the access token&lt;/h3&gt;

&lt;p&gt;Redeem the ID-JAG at the resource app’s authorization server’s token endpoint. The resource app’s authorization server handles the redemption request, not the IdP. This request uses the &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7523&quot;&gt;JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants (RFC 7523)&lt;/a&gt;. The ID-JAG is the assertion now, and you include the scopes required for the resource request. The scope matches the scopes requested in the ID-JAG request. The HTTP request looks something like:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;the-resource-server.example.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Basic &amp;lt;base64(resource_as_client_id:resource_as_client_secret)&amp;gt;&lt;/span&gt;

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&amp;amp;
assertion=&amp;lt;ID-JAG token&amp;gt;&amp;amp;
scope=todos.read
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the example HTTP request, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Basic&lt;/code&gt; scheme as a demo. Use the authorization scheme required by the resource application.&lt;/p&gt;

&lt;p&gt;The response payload contains the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; property, whose value is the access token. Use the access token to make the API resource request by using the value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header. For example:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;Authorization: Bearer &amp;lt;access_token&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;handle-token-expiration&quot;&gt;Handle token expiration&lt;/h3&gt;

&lt;p&gt;ID-JAG tokens have a short timeline by design. When it expires, request a shiny new ID-JAG from the refresh token. Refresh tokens also have a lifespan. If the IdP rejects the refresh token due to expiration (you’ll see an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid_grant&lt;/code&gt; error), then you need to obtain a new refresh token by having the user sign in via SSO again.&lt;/p&gt;

&lt;h2 id=&quot;making-cross-application-requests-from-your-saml-app-securely&quot;&gt;Making cross-application requests from your SAML app securely&lt;/h2&gt;

&lt;p&gt;With these steps complete, your SAML application is configured for Cross App Access. Agents can now authorize requests against your API while maintaining your existing production federation, eliminating the need for protocol migration.&lt;/p&gt;

&lt;p&gt;You can now use Okta to make cross-application requests with your SAML app.&lt;/p&gt;

&lt;h2 id=&quot;configure-your-xaa-saml-requesting-app-in-okta&quot;&gt;Configure your XAA SAML Requesting App in Okta&lt;/h2&gt;

&lt;p&gt;Let’s configure your SAML requester application in Okta. Before you begin, you’ll need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Values from your app (we’ll walk through the values needed below)&lt;/li&gt;
  &lt;li&gt;An Okta Integrator Free Plan account. &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Sign up for a new account&lt;/a&gt; to test out the XAA feature&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://xaa.dev/&quot;&gt;xaa.dev&lt;/a&gt; for testing your requesting app&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Cross App Access is an early access feature in Okta. New Integrator Free Plan account types include XAA support. If you have a paid Okta org plan and the following options are missing, contact your representative.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sign in to your Integrator Free Plan org and open the &lt;strong&gt;Admin Console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Enable AI Agent Identity Assertion:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Settings &amp;gt; Features &amp;gt; Early Access&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Find &lt;strong&gt;AI Agent Identity Assertion&lt;/strong&gt; and &lt;strong&gt;Agent to Agent Connections&lt;/strong&gt;, and enable both&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll need an Okta application representing your requesting app.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Create a new app integration&lt;/strong&gt; model, select &lt;strong&gt;SAML 2.0&lt;/strong&gt; and press &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: Enter a descriptive name for the app, for example, “Requesting App”.&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Single sign-on URL&lt;/strong&gt;: Use the ACS URL of your requestor app, e.g., “https://requester-app-uri/saml/acs”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;: Use the SP Entity of your requestor, e.g., “https://requester-app-uri/saml/metadata”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Name ID format&lt;/strong&gt;: select &lt;strong&gt;EmailAddress&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Application username&lt;/strong&gt;: select &lt;strong&gt;Email&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update application username on&lt;/strong&gt;: select &lt;strong&gt;Create and update&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Press &lt;strong&gt;Finish&lt;/strong&gt; to create the Okta SAML 2.0 application.&lt;/p&gt;

&lt;p&gt;After creating the app, you’ll see more configuration options for your Okta SAML 2.0 requesting app. You’ll make changes in more than one tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign On configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Sign On&lt;/strong&gt; tab. To the right, locate the &lt;strong&gt;View SAML setup instructions&lt;/strong&gt; under the header &lt;strong&gt;SAML Setup&lt;/strong&gt;, open the saml-doc file, and copy the &lt;strong&gt;Identity Provider Single Sign-On URL&lt;/strong&gt; and &lt;strong&gt;Identity Provider Issuer&lt;/strong&gt;. You will need these values in your requesting app setup. On the same page, make sure you download the X.509 certificate and add it back to your requester app as idp-cert.pem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assignments configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Assignments&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for your test user and select &lt;strong&gt;Assign&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Save and Go Back&lt;/strong&gt;, then select &lt;strong&gt;Done&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we have the requesting app registered, it’s time to register a resource app on the Okta platform. You’ll also need an Okta application representing the resource app.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Create a new app integration&lt;/strong&gt; model, select &lt;strong&gt;SAML 2.0&lt;/strong&gt; and press &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: Enter a descriptive name for the app, for example, “Resource App for Testing”.&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Add https://idp.xaa.dev/saml-resource/acs for &lt;strong&gt;Single sign-on URL&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Add https://idp.xaa.dev/saml-resource/metadata for &lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Name ID format&lt;/strong&gt;: select &lt;strong&gt;EmailAddress&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Application username&lt;/strong&gt;: select &lt;strong&gt;Email&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update application username on&lt;/strong&gt;: select &lt;strong&gt;Create and update&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;
Please note that we’re providing these for setup; they don’t constitute a working SSO connection to the resource app. Also, don’t assume the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; is an email address; it is whatever the customer’s SSO emits. Your matching set must remain consistent across your deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Press &lt;strong&gt;Finish&lt;/strong&gt; to create the Okta SAML 2.0 application.&lt;/p&gt;

&lt;p&gt;After creating the app, you’ll see more configuration options for your Okta SAML 2.0 app. You’ll make changes in the resource server tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Server extra configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Resource Server&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Enable XAA&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Issuer URL&lt;/strong&gt;: Add this value to your &lt;strong&gt;Issuer URL&lt;/strong&gt;: https://auth.resource.xaa.dev&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Assignments configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Assignments&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for your test user and select &lt;strong&gt;Assign&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Save and Go Back&lt;/strong&gt;, then select &lt;strong&gt;Done&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next step is to set up  &lt;a href=&quot;https://xaa.dev/&quot;&gt;xaa.dev&lt;/a&gt; for the resource app.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href=&quot;https://xaa.dev/developer/test-requesting-app?tab=saml&quot;&gt;Test your requesting app&lt;/a&gt;
Add your &lt;strong&gt;IdP issuer URL&lt;/strong&gt; as your Okta Integrator account ID (i.e., https://your-okta-domain.okta.com)
Put your email into the &lt;strong&gt;Test user identifier&lt;/strong&gt; example, name1234…@okta.com
Finally, select &lt;strong&gt;My ID-JAG is SAML-derived&lt;/strong&gt; and add your &lt;strong&gt;SAML IdP entityID&lt;/strong&gt; from the requesting app you created under the &lt;strong&gt;Sign On&lt;/strong&gt; tab &amp;gt; &lt;strong&gt;SAML Setup&lt;/strong&gt; &amp;gt; &lt;strong&gt;View SAML setup instructions&lt;/strong&gt;, open the saml-doc file, and find the &lt;strong&gt;Identity Provider Issuer&lt;/strong&gt; value (i.e., http://www.okta.com/&lt;app-id&gt;)
After all values are entered, click on the **Register**&lt;/app-id&gt;&lt;/p&gt;

&lt;h3 id=&quot;register-and-configure-the-ai-agent-in-okta&quot;&gt;Register and configure the AI Agent in Okta&lt;/h3&gt;

&lt;p&gt;With your Okta SAML 2.0 requesting app and the resource app configured, register a new AI Agent in Okta. The AI Agent configuration defines the relationship between the Okta SAML 2.0-requesting app you have and the resource application, which in this case is xaa.dev. You’ll configure credentials, add your requesting app as a delegated caller, and connect the resource app as a Resource Connection.&lt;/p&gt;

&lt;p&gt;In the Okta &lt;strong&gt;Admin Console&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Directory &amp;gt; AI Agents&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Register AI Agent &amp;gt; Register Manually&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enter a &lt;strong&gt;Name&lt;/strong&gt;, e.g., “Agent”&lt;/li&gt;
  &lt;li&gt;Select Register&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Select the AI Agent you just created to open its configuration. Configure the agent across the following tabs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;On the &lt;strong&gt;Owners&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Assign individual owners&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Search for and add yourself as an owner&lt;/li&gt;
      &lt;li&gt;Press &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Credentials&lt;/strong&gt; tab (select the agent you’ve just created to see this tab)
    &lt;ol&gt;
      &lt;li&gt;Copy the &lt;strong&gt;AI agent ID&lt;/strong&gt; and add the value as the &lt;strong&gt;Client ID&lt;/strong&gt; for your requesting app.&lt;/li&gt;
      &lt;li&gt;Back in Okta, select the &lt;strong&gt;Add Public Key&lt;/strong&gt;, and then press &lt;strong&gt;Generate new key&lt;/strong&gt;. Okta generates a key pair and displays the private key. Under &lt;strong&gt;PEM&lt;/strong&gt;, press &lt;strong&gt;Copy to clipboard&lt;/strong&gt;, then store the key securely on your end. You’ll paste this private key into the &lt;strong&gt;Private key (PKCS8 PEM or private JWK)&lt;/strong&gt; field in your requesting app, then save.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Also copy the kid and add it back to your requesting app&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Delegations&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Caller&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Search for the newly created Okta SAML requesting app&lt;/li&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Caller&lt;/strong&gt; to confirm&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Resource Connections&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Resource Connection&lt;/strong&gt;. Under the &lt;strong&gt;Resource&lt;/strong&gt; section, select &lt;strong&gt;Application&lt;/strong&gt; as the &lt;strong&gt;resource type&lt;/strong&gt;.
        &lt;ol&gt;
          &lt;li&gt;Under the &lt;strong&gt;Application&lt;/strong&gt; section, choose your &lt;strong&gt;App configured for AI Agent access&lt;/strong&gt; instance as the Resource app. (In this case, it should be the “Resources App”) from the dropdown menu and paste the &lt;strong&gt;Client ID&lt;/strong&gt; from xaa.dev. (It should look something similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byora...&lt;/code&gt;)&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;Under &lt;strong&gt;Scope Condition&lt;/strong&gt;, select &lt;strong&gt;Allow all&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Select &lt;strong&gt;Add&lt;/strong&gt; to confirm&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final step is to activate the AI agent. Go to Actions and select &lt;strong&gt;Activate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the AI Agent is active, the configuration is complete. All checkmarks on the agent configuration page must be green.&lt;/p&gt;

&lt;h3 id=&quot;test-the-saml-20-app&quot;&gt;Test the SAML 2.0 app&lt;/h3&gt;

&lt;p&gt;At this stage, you are all set to run the requesting app and verify the complete XAA flow it is supposed to perform.&lt;/p&gt;

&lt;h3 id=&quot;validate-the-xaa-connection-end-to-end&quot;&gt;Validate the XAA connection end-to-end&lt;/h3&gt;

&lt;p&gt;Once the flow is complete in the SAML 2.0 application, return to &lt;a href=&quot;https://xaa.dev/&quot;&gt;xaa.dev&lt;/a&gt;. In the &lt;strong&gt;Live verification&lt;/strong&gt; tab, a green &lt;strong&gt;Conformance passed&lt;/strong&gt; panel appears. This confirms all steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Auth Server Accepted your ID-JAG&lt;/li&gt;
  &lt;li&gt;Access token was issued&lt;/li&gt;
  &lt;li&gt;Resource Server accepted your access token&lt;/li&gt;
  &lt;li&gt;API call to the /api/todos/ was a success.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this stage, the JSON conformance log will have the complete details of the XAA flow.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-cross-app-access-saml-and-oauth-20&quot;&gt;Learn more about Cross App Access, SAML, and OAuth 2.0&lt;/h2&gt;

&lt;p&gt;If this guide helped you implement Cross App Access with SAML, explore these resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;📘 &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/apps/apps-cross-app-access.htm&quot;&gt;Cross App Access Documentation&lt;/a&gt;: Official guides for configuring and managing Cross App Access in production&lt;/li&gt;
  &lt;li&gt;🎙️ &lt;a href=&quot;https://www.youtube.com/watch?v=qKs4k5Y1x_s&quot;&gt;Developer Podcast on MCP and Cross App Access&lt;/a&gt;: Hear the backstory, use cases, and why this matters for developers&lt;/li&gt;
  &lt;li&gt;📋 &lt;a href=&quot;/blog/2026/07/06/submit-oin-xaa&quot;&gt;How to Build and List Secure Cross App Access (XAA) Connections on Okta Integration Network (OIN)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Identity 101:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/identity-101/whats-the-difference-between-oauth-openid-connect-and-saml/&quot;&gt;What’s the Difference Between OAuth, OpenID Connect, and SAML?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/en-in/identity-101/saml-vs-oauth/&quot;&gt;What are SAML, OAuth, and OIDC?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/identity-101/why-you-should-migrate-to-oauth-2-0-from-static-api-tokens/&quot;&gt;Why You Should Migrate to OAuth 2.0 From Static API Tokens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2023/07/27/enterprise-ready-getting-started&quot;&gt;How to Get Going with the On-Demand SaaS Apps Workshops&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow us on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt;, and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube&lt;/a&gt; channel. Leave a comment below if you have any questions!&lt;/p&gt;

</description>
        <pubDate>Fri, 17 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/17/xaa-saml-requester</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/17/xaa-saml-requester</guid>
      </item>
    
      <item>
        <title>Build a Secure C# MCP App with Cross App Access (XAA)</title>
        <description>&lt;p&gt;A few years ago, getting a user signed in to an application or multiple applications with Single Sign-On (SSO) was enough; OpenID Connect (OIDC) handled the login, JWTs carried the claims, and Proof Key for Code Exchange (PKCE) made it secure. Today, with evolving AI, agents act on behalf of users and seek multiple accesses across different resources to execute a task. And that is when you’ll hit the gap.&lt;/p&gt;

&lt;p&gt;The user has an identity, but the downstream service—like a Model Context Protocol (MCP) server, an API, or an agent tool has no way to trust it: the ID Token that proves the user’s identity for your app, not for that service. You need a way to take that identity and have it trusted further down the chain, in line with the org’s policy, without asking the user to log in again.&lt;/p&gt;

&lt;p&gt;Cross App Access (XAA) solves exactly that. The user authenticates once. The Identity Provider (IdP) evaluates the enterprise policy and issues a signed Identity Assertion. The downstream service exchanges that assertion for a scoped Bearer token.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore how Cross App Access (XAA) closes the trust gap, test the flow using an &lt;a href=&quot;https://xaa.dev&quot;&gt;XAA playground&lt;/a&gt;, and implement a secure MCP client in just a few lines of C# using our dedicated SDK.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#what-is-cross-app-access-xaa&quot; id=&quot;markdown-toc-what-is-cross-app-access-xaa&quot;&gt;What is Cross App Access (XAA)?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#implementing-xaa-with-the-c-mcp-sdk&quot; id=&quot;markdown-toc-implementing-xaa-with-the-c-mcp-sdk&quot;&gt;Implementing XAA with the C# MCP SDK&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#building-the-oidc-flow&quot; id=&quot;markdown-toc-building-the-oidc-flow&quot;&gt;Building the OIDC flow&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#automate-xaa-token-exchange-with-c-sdk&quot; id=&quot;markdown-toc-automate-xaa-token-exchange-with-c-sdk&quot;&gt;Automate XAA token exchange with C# SDK&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#connect-the-mcp-client-to-the-server&quot; id=&quot;markdown-toc-connect-the-mcp-client-to-the-server&quot;&gt;Connect the MCP client to the server&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#testing-your-c-mcp-app-with-xaadev&quot; id=&quot;markdown-toc-testing-your-c-mcp-app-with-xaadev&quot;&gt;Testing your C# MCP app with xaa.dev&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#run-your-c-mcp-app-with-xaadev&quot; id=&quot;markdown-toc-run-your-c-mcp-app-with-xaadev&quot;&gt;Run your C# MCP app with xaa.dev&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-secure-ai-agent-development-with-c-and-mcp&quot; id=&quot;markdown-toc-learn-more-about-secure-ai-agent-development-with-c-and-mcp&quot;&gt;Learn More About Secure AI Agent Development with C# and MCP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-is-cross-app-access-xaa&quot;&gt;What is Cross App Access (XAA)?&lt;/h2&gt;

&lt;p&gt;Before we start implementing and building the application, it is important to understand the mechanics of Cross App Access (XAA). At its core, XAA is an open standard that securely enables AI agents to act on behalf of a user and communicate with downstream applications without requiring constant, manual user consent.&lt;/p&gt;

&lt;p&gt;While the flow is sophisticated, it relies on two standard interactions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;RFC 8693 (Token Exchange): The requesting app exchanges an OIDC ID token for a JWT Authorization Grant (JAG) at the enterprise Identity Provider.&lt;/li&gt;
  &lt;li&gt;RFC 7523 (JWT Bearer Grant): The app presents this JAG to the MCP authorization server to receive a scoped access token for the resource.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The diagram below gives a complete flow for XAA and token exchanges.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/csharp-mcp-cross-app-access/xaa-flow-diagram-0b58037b82f0377b542342c29793380a93e8af44d2a10bf0c3d135257d37d2f1.jpg&quot; width=&quot;800&quot; alt=&quot;blog/csharp-mcp-cross-app-access/xaa-flow-diagram.jpg&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can find a complete flow diagram and explanation of XAA in the &lt;a href=&quot;/blog/2025/06/23/enterprise-ai&quot;&gt;“Integrate Your Enterprise AI Tools with Cross App Access”&lt;/a&gt; blog.&lt;/p&gt;

&lt;p&gt;In the next steps, let us understand how to implement the 4 steps of XAA using the MCP software development kit (SDK).&lt;/p&gt;

&lt;h2 id=&quot;implementing-xaa-with-the-c-mcp-sdk&quot;&gt;Implementing XAA with the C# MCP SDK&lt;/h2&gt;

&lt;p&gt;Building this manually would require you to manage complex cryptographic handshakes. Instead, we’ll use the C# MCP SDK; more details are available in the &lt;a href=&quot;https://github.com/oktadev/okta-csharp-mcp-sdk-example&quot;&gt;official GitHub repository&lt;/a&gt;. The C# MCP SDK’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityAssertionGrantProvider&lt;/code&gt; handles all the heavy lifting, abstracting these RFCs into a clean, developer-friendly interface that lets you focus on your agent logic rather than the authentication plumbing. Let us implement that step by step.&lt;/p&gt;

&lt;h3 id=&quot;building-the-oidc-flow&quot;&gt;Building the OIDC flow&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://asp.net&quot;&gt;ASP.NET&lt;/a&gt; Core has a built-in OIDC middleware that handles the full authentication flow, including PKCE. Since the package handles the implementation, all we have to do is configure our values.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddAuthentication&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddCookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cookie&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;xaa.auth&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddOpenIdConnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Authority&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:IdpBaseUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:ClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResponseType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UsePkce&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SaveTokens&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ID Token persisted into the auth cookie&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapInboundClaims&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;openid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;An important thing to note here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MapInboundClaims = false&lt;/code&gt;, which stops ASP.NET Core from remapping standard OIDC claim names to legacy WS-Federation names, keeping the token payload clean and predictable downstream.&lt;/p&gt;

&lt;h3 id=&quot;automate-xaa-token-exchange-with-c-sdk&quot;&gt;Automate XAA token exchange with C# SDK&lt;/h3&gt;

&lt;p&gt;This step marks the start of the XAA flow. With the SDK, instead of the user directly authorizing the MCP client, the app performs a two-hop token upgrade on the user’s behalf, and the MCP C# SDK’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdentityAssertionGrantProvider&lt;/code&gt; encapsulates both exchanges.&lt;/p&gt;

&lt;p&gt;The application presents the user’s ID token to the IdP and represents the user’s identity to the MCP server. The IdP responds with an ID-JAG, a short-lived token that grants access to the resource server. You then exchange this ID-JAG for an access token to access the server.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IdentityAssertionGrantProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IdentityAssertionGrantProviderOptions&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientId&lt;/span&gt;         &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:McpClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]!,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ClientSecret&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:McpClientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IdpTokenEndpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:IdpBaseUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IdpClientId&lt;/span&gt;      &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:ClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]!,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IdpClientSecret&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:ClientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Scope&lt;/span&gt;            &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;todos.read mcp.access&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// Called by the SDK when it needs the current user's ID Token&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;IdTokenCallback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;httpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAccessTokenAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resourceUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;           &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:McpServerUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]!),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;authorizationServerUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:AuthServerUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]!));&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;accessToken&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IdTokenCallback&lt;/code&gt;, the provider retrieves the user’s ID Token from the session.&lt;/p&gt;

&lt;h3 id=&quot;connect-the-mcp-client-to-the-server&quot;&gt;Connect the MCP client to the server&lt;/h3&gt;

&lt;p&gt;After the exchanges, this is the final part, where the client can now access the MCP server and use the resource data. The HTTP-based servers handle session negotiation and message framing.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;//Set up the transport with the exchanged access token&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;HttpClientTransport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClientTransportOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Endpoint&lt;/span&gt;          &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Xaa:McpServerUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]!),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;TransportMode&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpTransportMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StreamableHttp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;AdditionalHeaders&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Authorization&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;Bearer &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;accessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Initialize the MCP client&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;McpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transport&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;McpClient.CreateAsync&lt;/code&gt; performs the MCP initialization handshake, exchanging the protocol version and capabilities with the server before making any resource request. From here on, interacting with the server is straightforward.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ListResourcesAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//lists all the available URIs&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadResourceAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;todo0://todos&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;raw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Contents&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OfType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TextResourceContents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;()&lt;/span&gt;
          &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This app uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;todo0://todos&lt;/code&gt; to fetch from the xaa.dev resource server. The complete sample code for the app is available in the linked &lt;a href=&quot;https://github.com/oktadev/okta-csharp-mcp-sdk-example&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;testing-your-c-mcp-app-with-xaadev&quot;&gt;Testing your C# MCP app with xaa.dev&lt;/h2&gt;

&lt;p&gt;Now that we’ve stepped through the XAA flow, it’s time to test it. &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt; is a testing playground. It provides a standardized, functional environment that lets you verify your end-to-end flow immediately and serves as the bridge between your app and the downstream resource.&lt;/p&gt;

&lt;p&gt;To test out the application using the xaa.dev platform, follow the steps below to get the app registered as the requester app:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Go to &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt; and select the &lt;strong&gt;Register, test, and manage your requesting app&lt;/strong&gt; tab, then select &lt;strong&gt;Continue with your app&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Enter your email and click on &lt;strong&gt;Continue&lt;/strong&gt; and &lt;strong&gt;Register New App&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Enter the Redirect URI and Post-logout URI as below.&lt;/li&gt;
  &lt;li&gt;Click on the &lt;strong&gt;Add Resource&lt;/strong&gt; section and select &lt;strong&gt;ToDo MCP Server&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Click on &lt;strong&gt;Register App&lt;/strong&gt;, and your app is now registered as a requester app in the XAA flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should have your connections set up like in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/csharp-mcp-cross-app-access/xaa-dev-register-app-52c42303a5fbaf76de7e7384a24a47d1d283ff863a27bbd652e26cdfb8366df6.jpg&quot; alt=&quot;xaa.dev Register New App screen showing redirect URIs, resource connections, and todos.read and mcp.access scopes&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this example, the C# app is a requester app that fetches the to-do list from the Todo app and analyzes the tasks fetched.&lt;/p&gt;

&lt;h2 id=&quot;run-your-c-mcp-app-with-xaadev&quot;&gt;Run your C# MCP app with xaa.dev&lt;/h2&gt;

&lt;p&gt;At this point, you should have your application ready, running on port 5000, and connected to the XAA sample resource app. If not, you can clone the repo from the &lt;a href=&quot;https://github.com/oktadev/okta-csharp-mcp-sdk-example&quot;&gt;GitHub repository&lt;/a&gt; and add the following environment variables to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.json&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/oktadev/csharp-mcp-sdk-example.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;xaa-csharp-mcp-sdk-example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Xaa&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;your-client-id&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ClientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;your-client-secret&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;IdpBaseUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://idp.xaa.dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;McpClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;your-mcp-client-id&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;McpClientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;your-mcp-client-secret&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;AuthServerUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://auth.resource.xaa.dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;McpServerUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://mcp.xaa.dev/mcp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;RedirectUri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;     &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:5000/callback&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Logging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LogLevel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft.AspNetCore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;AllowedHosts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure to copy the correct client ID and secrets from xaa.dev. Once done, run the app.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5000/&lt;/code&gt;, and you should see the app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/csharp-mcp-cross-app-access/app-login-screen-14c247f8241eb8d9f27bd2e286750eb8a1e91efa796958338e2faf3b302e9c32.jpg&quot; alt=&quot;AI Productivity Assistant app home screen with a Continue with Enterprise SSO button&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Sign in to the app using a test email and provide a random verification code as below. Once you sign in, the XAA playground handles the login request; you can verify this by checking the URL on your screen.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/csharp-mcp-cross-app-access/verify-identity-screen-326af6d6b04385b11cf83831bd47f15325ee9eedd15b081ad7bef32c96eb08ae.jpg&quot; alt=&quot;Verify Your Identity screen showing a 6-digit code entry field with a demo mode notice to enter any 6 digits&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once done, you should see the analyzer app running the XAA flow, displaying all details and analyzing all To-Do tasks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/csharp-mcp-cross-app-access/app-dashboard-44b1c501920cac6b9a1af1b1eb1267dcef0df15f4ce66e5531048d32fbc6aa0a.jpg&quot; alt=&quot;AI Productivity Assistant dashboard showing completed XAA auth flow steps, the access token, token claims, and a to-do task list&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The MCP SDK and XAA simplify building and testing cross app access.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-secure-ai-agent-development-with-c-and-mcp&quot;&gt;Learn More About Secure AI Agent Development with C# and MCP&lt;/h2&gt;

&lt;p&gt;What you’ve seen here is the full XAA flow, starting from a user signing in with SSO to an AI agent securely fetching data from an MCP server running end-to-end in under 50 lines of C#.&lt;/p&gt;

&lt;p&gt;The XAA playground (xaa.dev) makes this tangible without any infrastructure overhead. The IdP, the Auth Server, the MCP resource server — it’s all there, ready for you to register your app, drop in the credentials, and watch the token exchanges happen in real time. That’s the fastest path from concept to a working implementation.&lt;/p&gt;

&lt;p&gt;If you wish to read further:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Test the full source for this demo in the &lt;a href=&quot;https://github.com/oktadev/okta-csharp-mcp-sdk-example&quot;&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Register your own app and explore the XAA playground at &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Read the deep dive on how XAA closes the enterprise trust gap in &lt;a href=&quot;/blog/2025/06/23/enterprise-ai&quot;&gt;Integrate Your Enterprise AI Tools with Cross App Access&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As AI agents take on more complex, multi-step tasks across organizational boundaries, XAA is the secure solution that enables this without compromising security or user experience. Now you have the tools to build it.&lt;/p&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube channel&lt;/a&gt; for more exciting content. We also want to hear from you about the topics you’d like to see and any questions you may have. Leave us a comment below!&lt;/p&gt;
</description>
        <pubDate>Thu, 16 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/16/csharp-mcp-cross-app-access</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/16/csharp-mcp-cross-app-access</guid>
      </item>
    
      <item>
        <title>Introducing Okta Journeys: A Better Way for Developers to Learn Identity</title>
        <description>&lt;p&gt;Learning identity management is hard enough. Navigating Okta’s documentation to build something shouldn’t be. If you’ve ever lost an afternoon stitching together how-to guides, product docs, and scattered blog posts just to figure out where to start, you’re not alone – and we’ve heard you, loudly and repeatedly.&lt;/p&gt;

&lt;p&gt;Today, we’re excited to announce the official launch of Journeys: a new way to navigate Okta documentation built around the tasks you’re actually trying to accomplish.&lt;/p&gt;

&lt;h2 id=&quot;what-are-okta-journeys-for-developers&quot;&gt;What are Okta Journeys for developers?&lt;/h2&gt;

&lt;p&gt;A Journey is a curated, expert-driven, end-to-end guide built around a small-to-medium-sized development project. Rather than sending you to find a single document and piece the rest together yourself, each Journey walks you through the entire project, from foundational concepts to completion.&lt;/p&gt;

&lt;p&gt;Journeys address the most frequent questions we’ve heard from developers. Every Journey includes both brand-new material and revised content to ensure that what you’re reading is accurate, up to date, and genuinely useful.&lt;/p&gt;

&lt;p&gt;Each Journey organizes content into three main sections.&lt;/p&gt;

&lt;h3 id=&quot;learn-identity-foundations&quot;&gt;Learn identity foundations&lt;/h3&gt;

&lt;p&gt;Before you write a single line of code, it helps to know the terrain. The Learn section anchors the broad “identity” concept, covering foundational knowledge including Okta features, software development kits (SDKs), and application programming interfaces (APIs) relevant to your task. Whether you’re new to Okta or just unfamiliar with a specific area, this section gives you the vocabulary and mental model you need to make informed decisions. It ensures you’re not just following steps, but truly understanding the technology and concepts underlying your project.&lt;/p&gt;

&lt;h3 id=&quot;plan-your-customer-identity-implementation&quot;&gt;Plan your customer identity implementation&lt;/h3&gt;

&lt;p&gt;Good implementations start with good planning. The Plan section walks you through the key decision points to consider before you begin – from the pros and cons of migration strategies and deployment models to configuration options, rate limits, and key performance indicators (KPIs). These are all common concerns from the field. Decisions made here shape everything that follows, so you won’t discover them for the first time mid-build.&lt;/p&gt;

&lt;h3 id=&quot;build-and-implement-identity-solutions&quot;&gt;Build and implement identity solutions&lt;/h3&gt;

&lt;p&gt;This is where everything comes together. The Build section presents a carefully curated collection of resources, organized to guide you through your project from start to finish. No more hunting across technical content channels to find the correct how-to guide, configuration advice, Knowledge Base (KB) article, blog post, API endpoint details, or videos. Everything you need is in one place, in the right order.&lt;/p&gt;

&lt;p&gt;Every Journey covers the Okta-recommended approach and adds common alternatives when practical, because we know one size doesn’t always fit all.&lt;/p&gt;

&lt;h2 id=&quot;six-okta-journeys-available-now&quot;&gt;Six Okta Journeys available now&lt;/h2&gt;

&lt;p&gt;The first six Journeys are live today, targeting Okta Customer Identity (OCI) builders developing and securing customer-facing portals. If you’re working on user authentication, registration, company branding, or user management, these Journeys are for you.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/journeys/&quot;&gt;Explore Journeys&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;whats-next-okta-for-ai-agents-and-more&quot;&gt;What’s next: Okta for AI agents and more&lt;/h2&gt;

&lt;p&gt;This is just the beginning. We’re already building new Journeys to tackle high-impact, emerging scenarios, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Okta for AI Agents: Detect, identify, manage, register, and govern your AI agents.&lt;/li&gt;
  &lt;li&gt;Building for the Okta Integration Network (OIN): Journeys for developers building different types of integrations for the &lt;a href=&quot;https://www.okta.com/okta-integration-network/&quot;&gt;Okta Integration Network&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Classic Engine to Okta Identity Engine Migration: Update your org to Okta Identity Engine and remove the Classic Engine-specific code from your apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are committed to continuously expanding this library so that, no matter your development goal, you have a clear, expert-guided path to success.&lt;/p&gt;

&lt;h2 id=&quot;tell-us-what-you-think&quot;&gt;Tell us what you think&lt;/h2&gt;

&lt;p&gt;These Journeys will fundamentally improve your development experience. Explore them, share your feedback, and let us know what Journeys you’d like to see next. Use the feedback tab on the Journey page, reach out to us on the &lt;a href=&quot;https://devforum.okta.com/&quot;&gt;Okta Developer Forums&lt;/a&gt;, or find us on socials.&lt;/p&gt;

&lt;p&gt;Happy building.&lt;/p&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube channel&lt;/a&gt; and &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt;for more exciting content. We also want to hear from you about the topics you’d like to see and any questions you may have. Leave us a comment below!&lt;/p&gt;
</description>
        <pubDate>Tue, 07 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/07/okta-journeys-for-developers</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/07/okta-journeys-for-developers</guid>
      </item>
    
      <item>
        <title>How to Build and List Secure Cross App Access (XAA) Connections on Okta Integration Network (OIN)</title>
        <description>&lt;p&gt;AI agents have evolved from novelties into active participants in enterprise workflows. They now operate across systems, reading data, executing actions, and calling APIs on behalf of users.&lt;/p&gt;

&lt;p&gt;This evolution creates a new security hurdle for enterprises. Software and agents need to connect without relying on static API keys, scattered OAuth consent, or unmanaged integrations. Cross App Access (XAA) addresses this by bringing these connections under the enterprise identity layer.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#what-is-cross-app-access-xaa&quot; id=&quot;markdown-toc-what-is-cross-app-access-xaa&quot;&gt;What is Cross App Access (XAA)?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-cross-app-access-xaa-matters-for-isvs-and-their-customers&quot; id=&quot;markdown-toc-why-cross-app-access-xaa-matters-for-isvs-and-their-customers&quot;&gt;Why Cross App Access (XAA) matters for ISVs and their customers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#prerequisites-for-supporting-cross-app-access-xaa-in-your-app&quot; id=&quot;markdown-toc-prerequisites-for-supporting-cross-app-access-xaa-in-your-app&quot;&gt;Prerequisites for supporting Cross App Access (XAA) in your app&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#implementation-and-testing-guide-for-cross-app-access-xaa-with-okta-as-idp&quot; id=&quot;markdown-toc-implementation-and-testing-guide-for-cross-app-access-xaa-with-okta-as-idp&quot;&gt;Implementation and testing guide for Cross App Access (XAA) with Okta as IdP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#getting-listed-in-the-okta-integration-network-oin&quot; id=&quot;markdown-toc-getting-listed-in-the-okta-integration-network-oin&quot;&gt;Getting listed in the Okta Integration Network (OIN)&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#xaa-enablement-questionnaire&quot; id=&quot;markdown-toc-xaa-enablement-questionnaire&quot;&gt;XAA enablement questionnaire&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#what-happens-next-after-you-submit-your-oin-and-xaa-request&quot; id=&quot;markdown-toc-what-happens-next-after-you-submit-your-oin-and-xaa-request&quot;&gt;What happens next after you submit your OIN and XAA request?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#need-help-with-your-cross-app-access-xaa-submission&quot; id=&quot;markdown-toc-need-help-with-your-cross-app-access-xaa-submission&quot;&gt;Need help with your Cross App Access (XAA) submission?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-cross-app-access-and-the-okta-integration-network&quot; id=&quot;markdown-toc-learn-more-about-cross-app-access-and-the-okta-integration-network&quot;&gt;Learn more about Cross App Access and the Okta Integration Network&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-is-cross-app-access-xaa&quot;&gt;What is Cross App Access (XAA)?&lt;/h2&gt;

&lt;p&gt;Cross App Access (XAA) is an identity framework that secures token-based communication when software or AI agents request data from external ecosystems, and those apps have an established trust relationship with an Identity Provider (IdP). It replaces insecure, long-lived API keys or hardcoded secrets with a pattern for real-time identity propagation between different application vendors.&lt;/p&gt;

&lt;p&gt;Watch the video below to see Cross App Access (XAA) in action:&lt;/p&gt;

&lt;div class=&quot;jekyll-youtube-plugin&quot; style=&quot;text-align: center; margin-bottom: 1.25rem&quot;&gt;
            &lt;iframe width=&quot;700&quot; height=&quot;394&quot; style=&quot;max-width: 100%&quot; src=&quot;https://www.youtube.com/embed/3VLzeT1EGrg&quot; allowfullscreen=&quot;&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;
        &lt;/div&gt;

&lt;p&gt;A concrete way to think about it: consider an employee using an AI assistant to prepare for a meeting. The assistant might need to pull tasks from one application, notes from another, and account details from a third. Without XAA, every connection might require separate user permissions, distinct API keys, or custom administrative work.&lt;/p&gt;

&lt;p&gt;XAA allows enterprises to centrally determine which apps connect, which scopes they request, and which users gain access. This streamlines the user experience while giving the enterprise the control it requires.&lt;/p&gt;

&lt;h2 id=&quot;why-cross-app-access-xaa-matters-for-isvs-and-their-customers&quot;&gt;Why Cross App Access (XAA) matters for ISVs and their customers&lt;/h2&gt;

&lt;p&gt;Every time an AI agent or application accesses another system without a proper identity handshake, customers face security and compliance risks. XAA eliminates this gap, providing IT teams with the visibility and audit trails they need to govern cross-app identity flows.&lt;/p&gt;

&lt;p&gt;For ISVs, XAA adoption simplifies the integration process in enterprise environments where security and visibility remain top priorities. It demonstrates trust to buyers, showing that your application fits into a secure, modern ecosystem. As enterprises increase their expectations for secure integrations, XAA readiness becomes a powerful competitive differentiator.&lt;/p&gt;

&lt;p&gt;At a high level, XAA involves three distinct roles:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Requesting app&lt;/strong&gt;: the requesting app acts on behalf of the user but does not mint the identity assertion itself. Instead, it receives an ID token or Security Assertion Markup Language (SAML) assertion from the identity provider and exchanges that assertion for an Identity Assertion Authorization Grant (ID-JAG), a JSON Web Token (JWT).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resource app&lt;/strong&gt;: the resource app owns the API or data. It validates the incoming ID-JAG and issues a scoped access token if the request is valid.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Identity Provider&lt;/strong&gt;: authenticates the subject, evaluates access policies, and generates the necessary ID-JAG. &lt;a href=&quot;https://okta.com&quot;&gt;Okta&lt;/a&gt; fills this role in an XAA deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;prerequisites-for-supporting-cross-app-access-xaa-in-your-app&quot;&gt;Prerequisites for supporting Cross App Access (XAA) in your app&lt;/h2&gt;

&lt;p&gt;Before you start building, ensure you have these prerequisites in place:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Defined app role&lt;/strong&gt;: determine if your app will function as a requesting app, a resource app, or both.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Authorization server support&lt;/strong&gt;: if your app acts as a resource app, your authorization server must validate the ID-JAG and issue a scoped access token for the protected resource.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scopes and protected resources&lt;/strong&gt;: if you are building a resource app, clearly define the APIs and scopes available to requesting apps.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Okta Integrator Free Plan org&lt;/strong&gt;: use this org to build, test, and submit your integration. You can &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;register for a new account&lt;/a&gt;. After signing up, email &lt;a href=&quot;mailto:developers@okta.com&quot;&gt;developers@okta.com&lt;/a&gt; to enable the XAA feature for your org.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Single Sign-On (SSO) integration&lt;/strong&gt;: XAA relies on the trust your existing SSO already establishes. Ensure your app supports OpenID Connect (OIDC) or SAML SSO with Okta.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Tested workflows&lt;/strong&gt;: you must demonstrate that XAA works with Okta as the IdP before requesting XAA enablement for your Okta Integration Network (OIN) integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;implementation-and-testing-guide-for-cross-app-access-xaa-with-okta-as-idp&quot;&gt;Implementation and testing guide for Cross App Access (XAA) with Okta as IdP&lt;/h2&gt;

&lt;p&gt;To begin development, select the guide corresponding to your application’s authentication protocol:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If your resource app uses SAML for SSO, follow our &lt;a href=&quot;/blog/2026/07/03/cross-app-access-saml&quot;&gt;SAML resource app implementation guide&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;If your requesting app uses SAML for SSO, follow our &lt;a href=&quot;/blog/2026/07/17/xaa-saml-requester&quot;&gt;SAML requesting app implementation guide&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;If your app uses OIDC for SSO, refer to the &lt;a href=&quot;/blog/2025/09/03/cross-app-access&quot;&gt;OIDC implementation guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To verify your configuration, demonstrate a successful token exchange:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Requesting apps&lt;/strong&gt;: provide evidence that you successfully obtained an ID token via OIDC SSO and used it to mint an ID-JAG.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resource apps&lt;/strong&gt;: show that your app received an ID-JAG and successfully exchanged it for a scoped access token.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Completing these tests is a hard requirement for approval; please ensure they are successful before submitting your integration.&lt;/p&gt;

&lt;h2 id=&quot;getting-listed-in-the-okta-integration-network-oin&quot;&gt;Getting listed in the Okta Integration Network (OIN)&lt;/h2&gt;

&lt;p&gt;Listing your integration on the &lt;a href=&quot;https://www.okta.com/integrations/&quot;&gt;Okta Integration Network (OIN)&lt;/a&gt; helps customers discover and trust it. Because XAA relies on trust that your existing single sign-on (SSO) configuration already establishes, you must list an OIDC or SAML SSO integration on the OIN before proceeding.&lt;/p&gt;

&lt;p&gt;If your app is not yet listed, prioritize that submission. Submit your SSO app through the &lt;a href=&quot;https://developer.okta.com/docs/guides/submit-oin-app/openidconnect/main/&quot;&gt;OIN Wizard&lt;/a&gt;. This &lt;a href=&quot;https://developer.okta.com/docs/guides/okta-integration-network/&quot;&gt;Okta Integration Network guide&lt;/a&gt; walks you through everything you need to get your integration listed, from app metadata to SSO configuration. Once you submit your SSO app for review, you can begin the XAA enablement request in parallel.&lt;/p&gt;

&lt;p&gt;XAA submissions currently require a manual step: you need to contact Okta directly to indicate that your submission includes XAA support. When your app successfully passes token exchange tests, email the Okta team at &lt;a href=&quot;mailto:oin@okta.com&quot;&gt;oin@okta.com&lt;/a&gt; to request XAA enablement. To speed up the review and avoid additional rounds of configuration, include the completed questionnaire below in your email.&lt;/p&gt;

&lt;p&gt;Use this subject line: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Request to enable XAA support for &amp;lt;App Name&amp;gt; on OIN&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;xaa-enablement-questionnaire&quot;&gt;XAA enablement questionnaire&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;General app details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;App Name:&lt;/li&gt;
  &lt;li&gt;Okta Integrator Org domain:&lt;/li&gt;
  &lt;li&gt;SSO Mode: OIDC / SAML&lt;/li&gt;
  &lt;li&gt;XAA App Role: Requesting app / Resource app / Both&lt;/li&gt;
  &lt;li&gt;Existing OIN App Link, if already published&lt;/li&gt;
  &lt;li&gt;Submission Type: new OIN submission / update to an existing OIN app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Requesting app details:&lt;/strong&gt; &lt;em&gt;fill this out if your app acts as a requesting app.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Resource Registration Pairs: for each resource app you connect with, provide:
    &lt;ul&gt;
      &lt;li&gt;Resource app name&lt;/li&gt;
      &lt;li&gt;Authorization Server Issuer URL&lt;/li&gt;
      &lt;li&gt;Registered Client ID&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Supported Scopes: list the scopes your app requests from resource apps.&lt;/li&gt;
  &lt;li&gt;Client ID Metadata Documents (CIMD) Support:
    &lt;ul&gt;
      &lt;li&gt;If yes, please provide the CIMD URL&lt;/li&gt;
      &lt;li&gt;No&lt;/li&gt;
      &lt;li&gt;Planned future support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resource app details:&lt;/strong&gt; &lt;em&gt;fill this out if your app acts as a resource app.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Global Issuer URL: the endpoint that processes ID-JAG token exchange requests&lt;/li&gt;
  &lt;li&gt;Protected Resource Identifiers: URLs of the APIs your app exposes (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://api.yourdomain.com/v1&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Supported OAuth Scopes: scopes allowed for XAA token exchange (e.g., openid, read, write)&lt;/li&gt;
  &lt;li&gt;CIMD Support:
    &lt;ul&gt;
      &lt;li&gt;If yes, please provide the CIMD URL&lt;/li&gt;
      &lt;li&gt;No&lt;/li&gt;
      &lt;li&gt;Planned future support&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Well-Known Host Endpoints: specify whether you host either of the following:
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known/oauth-authorization-server&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.well-known/oauth-protected-resources&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing details:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you’re a requesting app: confirm that you generated successful token exchange logs. The Okta operations team can verify these via internal telemetry parameters. To help the team validate quickly, also include:
    &lt;ul&gt;
      &lt;li&gt;Test org&lt;/li&gt;
      &lt;li&gt;App name&lt;/li&gt;
      &lt;li&gt;Test user or test account used&lt;/li&gt;
      &lt;li&gt;Resource app tested against&lt;/li&gt;
      &lt;li&gt;Scopes requested&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;If you’re a resource app: attach a screenshot or a short video showing successful ID-JAG validation and scoped access token exchange logs directly from your authorization server. These cannot be verified externally, so the evidence needs to come from you. Please include:
    &lt;ul&gt;
      &lt;li&gt;Resource endpoint tested&lt;/li&gt;
      &lt;li&gt;Scopes granted&lt;/li&gt;
      &lt;li&gt;Token exchange result&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-happens-next-after-you-submit-your-oin-and-xaa-request&quot;&gt;What happens next after you submit your OIN and XAA request?&lt;/h3&gt;

&lt;p&gt;Once you submit your request, the Okta team reviews your SSO submission, XAA metadata, and testing evidence. If your app is already on the OIN, we will update the existing listing after approval. For new integrations, we complete the standard SSO review before publishing your app with XAA support. If the team needs further clarification or additional testing evidence, we will contact you directly.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-your-cross-app-access-xaa-submission&quot;&gt;Need help with your Cross App Access (XAA) submission?&lt;/h2&gt;

&lt;p&gt;Please reach out to &lt;a href=&quot;mailto:developers@okta.com&quot;&gt;developers@okta.com&lt;/a&gt; for help. You can also find answers and connect with peers in our &lt;a href=&quot;https://devforum.okta.com/&quot;&gt;developer community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;XAA advances how apps and agents interact securely. By supporting this standard and listing your integration on the OIN, you help enterprise customers adopt AI-driven automation with better governance, stronger identity control, and increased confidence.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-cross-app-access-and-the-okta-integration-network&quot;&gt;Learn more about Cross App Access and the Okta Integration Network&lt;/h2&gt;

&lt;p&gt;If this guide helped you plan your OIN and XAA submission, explore these resources next:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;📘 &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/apps/apps-cross-app-access.htm&quot;&gt;Cross App Access documentation&lt;/a&gt;: official guides for configuring and managing Cross App Access in production.&lt;/li&gt;
  &lt;li&gt;📄 &lt;a href=&quot;https://developer.okta.com/docs/guides/okta-integration-network/&quot;&gt;Okta Integration Network documentation&lt;/a&gt;: everything you need to get your SSO integration listed on the OIN.&lt;/li&gt;
  &lt;li&gt;🔐 &lt;a href=&quot;/blog/2026/07/03/cross-app-access-saml&quot;&gt;Enabling Cross App Access for SAML-Based Resource Apps&lt;/a&gt;: the implementation guide for SAML SSO resource apps.&lt;/li&gt;
  &lt;li&gt;🔐 &lt;a href=&quot;/blog/2026/07/17/xaa-saml-requester&quot;&gt;Enable Your SAML Requesting App for Cross App Access&lt;/a&gt;: the implementation guide for SAML SSO requesting apps.&lt;/li&gt;
  &lt;li&gt;🔑 &lt;a href=&quot;/blog/2025/09/03/cross-app-access&quot;&gt;Build secure agent-to-app connections with Cross App Access (XAA)&lt;/a&gt;: the implementation guide for OIDC SSO apps.&lt;/li&gt;
  &lt;li&gt;🎙️ &lt;a href=&quot;https://www.youtube.com/watch?v=qKs4k5Y1x_s&quot;&gt;Developer podcast on MCP and Cross App Access&lt;/a&gt;: hear the backstory, use cases, and why this matters for developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow us on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt;, and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube&lt;/a&gt; channel. Leave a comment below if you have any questions!&lt;/p&gt;
</description>
        <pubDate>Mon, 06 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/06/submit-oin-xaa</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/06/submit-oin-xaa</guid>
      </item>
    
      <item>
        <title>Enabling Cross App Access for SAML-Based Resource Apps</title>
        <description>&lt;p&gt;If you currently federate enterprise customers using Security Assertion Markup Language (SAML) and want to allow applications to access your API without migrating to OpenID Connect (OIDC), this Cross App Access (XAA) guide is for you.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/&quot;&gt;Identity Assertion Authorization Grant specification&lt;/a&gt;, the basis of XAA, was originally designed with OIDC in mind. To use it in SAML applications, you must accommodate specific security and uniqueness requirements. This guide details what you need to support and how to verify SAML-derived claims at your resource authorization server.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#how-xaa-in-saml-works&quot; id=&quot;markdown-toc-how-xaa-in-saml-works&quot;&gt;How XAA in SAML works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#analyzing-the-id-jag-claims&quot; id=&quot;markdown-toc-analyzing-the-id-jag-claims&quot;&gt;Analyzing the ID-JAG claims&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#xaa-implementation-checklist-for-saml-federated-applications&quot; id=&quot;markdown-toc-xaa-implementation-checklist-for-saml-federated-applications&quot;&gt;XAA implementation checklist for SAML-federated applications&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#mapping-user-identity-in-the-saml-nameid-attribute&quot; id=&quot;markdown-toc-mapping-user-identity-in-the-saml-nameid-attribute&quot;&gt;Mapping user identity in the SAML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; attribute&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#validating-the-id-jag-and-resolving-the-user&quot; id=&quot;markdown-toc-validating-the-id-jag-and-resolving-the-user&quot;&gt;Validating the ID-JAG and resolving the user&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#issuing-the-access-token&quot; id=&quot;markdown-toc-issuing-the-access-token&quot;&gt;Issuing the access token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#updating-authorization-server-metadata&quot; id=&quot;markdown-toc-updating-authorization-server-metadata&quot;&gt;Updating authorization server metadata&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#making-cross-application-requests-from-your-saml-app-securely&quot; id=&quot;markdown-toc-making-cross-application-requests-from-your-saml-app-securely&quot;&gt;Making cross-application requests from your SAML app securely&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configure-your-xaa-saml-app-in-okta&quot; id=&quot;markdown-toc-configure-your-xaa-saml-app-in-okta&quot;&gt;Configure your XAA SAML App in Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-saml-20-resource-app-in-okta&quot; id=&quot;markdown-toc-create-the-saml-20-resource-app-in-okta&quot;&gt;Create the SAML 2.0 resource app in Okta&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-saml-20-requester-app-for-testing&quot; id=&quot;markdown-toc-create-a-saml-20-requester-app-for-testing&quot;&gt;Create a SAML 2.0 requester app for testing&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#register-and-configure-the-ai-agent-in-okta&quot; id=&quot;markdown-toc-register-and-configure-the-ai-agent-in-okta&quot;&gt;Register and configure the AI Agent in Okta&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#verify-your-okta-xaa-setup-on-xaadev&quot; id=&quot;markdown-toc-verify-your-okta-xaa-setup-on-xaadev&quot;&gt;Verify your Okta XAA setup on xaa.dev&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#configure-saml-sso&quot; id=&quot;markdown-toc-configure-saml-sso&quot;&gt;Configure SAML SSO&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#confirm-the-saml-assertion-exchange-for-a-refresh-token&quot; id=&quot;markdown-toc-confirm-the-saml-assertion-exchange-for-a-refresh-token&quot;&gt;Confirm the SAML Assertion exchange for a refresh token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#verify-the-refresh-token-exchange-for-an-id-jag-token&quot; id=&quot;markdown-toc-verify-the-refresh-token-exchange-for-an-id-jag-token&quot;&gt;Verify the refresh token exchange for an ID-JAG token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#redeem-the-id-jag-for-an-access-token-at-the-resource-authorization-server&quot; id=&quot;markdown-toc-redeem-the-id-jag-for-an-access-token-at-the-resource-authorization-server&quot;&gt;Redeem the ID-JAG for an access token at the resource authorization server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#call-the-resource-api-with-the-access-token&quot; id=&quot;markdown-toc-call-the-resource-api-with-the-access-token&quot;&gt;Call the resource API with the access token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#prove-the-xaa-connection-end-to-end&quot; id=&quot;markdown-toc-prove-the-xaa-connection-end-to-end&quot;&gt;Prove the XAA connection end-to-end&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#takeaways-for-implementors-who-have-both-oidc-and-saml-apps&quot; id=&quot;markdown-toc-takeaways-for-implementors-who-have-both-oidc-and-saml-apps&quot;&gt;Takeaways for implementors who have both OIDC and SAML apps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-cross-app-access-saml-and-oauth-20&quot; id=&quot;markdown-toc-learn-more-about-cross-app-access-saml-and-oauth-20&quot;&gt;Learn more about Cross App Access, SAML, and OAuth 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-xaa-in-saml-works&quot;&gt;How XAA in SAML works&lt;/h2&gt;

&lt;p&gt;When an agent (like one running in Claude) needs API access, it presents an &lt;strong&gt;Identity Assertion Authorization Grant (ID-JAG)&lt;/strong&gt;. The ID-JAG is a short-lived JSON Web Token (JWT) issued by the customer’s Identity Provider (IdP) for your authorization server. Your resource server accepts the token, identifies the user, and issues your own access token, all while leaving the customer’s existing SAML integration untouched.&lt;/p&gt;

&lt;p&gt;The sequence diagram shown below describes the SAML XAA flow. Notice that the SAML SSO flow stays the same; the only change is the section highlighted with the comment “Your Resource Authorization Server (AS): redeem and resolve”. You’ll make a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; request to your resource’s authorization server with the ID-JAG, resolve the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; to return an access token that you’ll use for resource requests.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/xaa-saml-sequence-diagram-f2db13d4cb32e0de677e0ffbb409f8a89c963d479ade19b99a3aaf5f0953e139.svg&quot; alt=&quot;Sequence diagram showing SAML SSO between the user and Okta IdP, two OAuth token exchanges producing a refresh token and then an ID-JAG, and the resource authorization server redeeming the ID-JAG and resolving the SAML NameID before issuing an access token used to call the API.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;You are not processing SAML here. The only artifact crossing from the IdP to your domain is the ID-JAG. All SAML-related tasks, such as SSO, assertion handling, and subject derivation, happen upstream. Your responsibility is to validate the ID-JAG, redeem it for an access token, and resolve the user from the claims.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;analyzing-the-id-jag-claims&quot;&gt;Analyzing the ID-JAG claims&lt;/h2&gt;

&lt;p&gt;When you decode the ID-JAG, you’ll see claims in the header and payload that impact how you process the access request:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;typ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;oauth-id-jag+jwt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://atko.okta.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;00u1a2b3c4D5e6F7g8h9&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;format&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;saml-nameid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;issuer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://www.okta.com/exk1fcia8zMValiD0h8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nameid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;alice@atko.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nameid_format&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sp_name_qualifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://chat.example/saml/metadata&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://auth.chat.example&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;client_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0oa8claudeMcpAtYourAS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;alice@atko.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;chat:read chat:write&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;jti&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;id-jag-7f3c9a21b8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Focus on these key claims noted in the decoded ID-JAG payload:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt;&lt;/strong&gt;: This is the primary field for user resolution&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt;&lt;/strong&gt;: Indicates the endpoint URL for the resource authorization server&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt;&lt;/strong&gt;: This is the client’s ID at your resource authorization server, which might differ from its ID at the IdP&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt;&lt;/strong&gt;: Recommended by the specification for just-in-time provisioning if the user has not yet signed in&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jti&lt;/code&gt;&lt;/strong&gt;: This is the unique ID for the ID-JAG JWT that prevents replay attacks within the validity window&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;xaa-implementation-checklist-for-saml-federated-applications&quot;&gt;XAA implementation checklist for SAML-federated applications&lt;/h2&gt;

&lt;p&gt;To fully support Cross App Access, implement these five steps in sequence:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#how-xaa-in-saml-works&quot;&gt;How XAA in SAML works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#analyzing-the-id-jag-claims&quot;&gt;Analyzing the ID-JAG claims&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#xaa-implementation-checklist-for-saml-federated-applications&quot;&gt;XAA implementation checklist for SAML-federated applications&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#mapping-user-identity-in-the-saml-nameid-attribute&quot;&gt;Mapping user identity in the SAML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; attribute&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#validating-the-id-jag-and-resolving-the-user&quot;&gt;Validating the ID-JAG and resolving the user&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#issuing-the-access-token&quot;&gt;Issuing the access token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#updating-authorization-server-metadata&quot;&gt;Updating authorization server metadata&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#making-cross-application-requests-from-your-saml-app-securely&quot;&gt;Making cross-application requests from your SAML app securely&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configure-your-xaa-saml-app-in-okta&quot;&gt;Configure your XAA SAML App in Okta&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-saml-20-resource-app-in-okta&quot;&gt;Create the SAML 2.0 resource app in Okta&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-a-saml-20-requester-app-for-testing&quot;&gt;Create a SAML 2.0 requester app for testing&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#register-and-configure-the-ai-agent-in-okta&quot;&gt;Register and configure the AI Agent in Okta&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#verify-your-okta-xaa-setup-on-xaadev&quot;&gt;Verify your Okta XAA setup on xaa.dev&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#configure-saml-sso&quot;&gt;Configure SAML SSO&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#confirm-the-saml-assertion-exchange-for-a-refresh-token&quot;&gt;Confirm the SAML Assertion exchange for a refresh token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#verify-the-refresh-token-exchange-for-an-id-jag-token&quot;&gt;Verify the refresh token exchange for an ID-JAG token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#redeem-the-id-jag-for-an-access-token-at-the-resource-authorization-server&quot;&gt;Redeem the ID-JAG for an access token at the resource authorization server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#call-the-resource-api-with-the-access-token&quot;&gt;Call the resource API with the access token&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#prove-the-xaa-connection-end-to-end&quot;&gt;Prove the XAA connection end-to-end&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#takeaways-for-implementors-who-have-both-oidc-and-saml-apps&quot;&gt;Takeaways for implementors who have both OIDC and SAML apps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-cross-app-access-saml-and-oauth-20&quot;&gt;Learn more about Cross App Access, SAML, and OAuth 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;mapping-user-identity-in-the-saml-nameid-attribute&quot;&gt;Mapping user identity in the SAML &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; attribute&lt;/h3&gt;

&lt;p&gt;Unlike OIDC apps, which typically resolve users from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim, SAML-federated apps do not have a corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim in their SAML assertion. Consequently, they often lack a direct way to map users without using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;You must compare every member of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;saml-nameid&lt;/code&gt; identifier used as a subject key for a given SAML issuer. Do not resolve based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; alone unless your local policy permits it.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; field alone doesn’t uniquely identify a user, since two organizations could each have an employee named Alex Chen. This problem is analogous to resolving user uniqueness in multi-tenant applications.&lt;/p&gt;

&lt;p&gt;Resolve on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sp_name_qualifier&lt;/code&gt; together; the combination of both fields provides the unique user identity required.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Don’t assume the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; is an email address; it is whatever the customer’s SSO emits. Your matching set must remain consistent across your deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;validating-the-id-jag-and-resolving-the-user&quot;&gt;Validating the ID-JAG and resolving the user&lt;/h3&gt;

&lt;p&gt;The client posts the ID-JAG as a JWT authorization grant and authenticates with its credentials at your server. Below is an example HTTP request for requesting an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/oauth2/v1/token&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;chat.example&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Basic &amp;lt;base64(client_id:client_secret)&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/x-www-form-urlencoded&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;grant_type=urn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ietf:params:oauth:grant-type:jwt-bearer&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;&amp;amp;assertion=eyJ0eXAiOiJvYXV0aC1pZC1qYWcrand0...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before processing, you must bind the ID-JAG’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; to a registered SAML connection to prevent forgery.&lt;/p&gt;

&lt;p&gt;If you verify the signature before checking the issuer binding, an attacker could potentially create their own IdP, sign a token, and use your customer’s SAML issuer as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Always resolve the connection from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; first, then verify the signature against that connection’s key. You’ll compare this using the JSON Web Key Set (JWKS) metadata.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/idjag-validation-order-690e0fac947f453792a7e365b3fa011b3fa58dbf447e0974c8923967598a93db.jpg&quot; alt=&quot;ID-JAG validation order&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Below is the pseudocode for implementing the validation and resolving a user:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&quot;https://atko.okta.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jwks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;s&quot;&gt;&quot;https://atko.okta.com/oauth2/v1/keys&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;samlIssuer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;s&quot;&gt;&quot;http://www.okta.com/exk1fcia8zMValiD0h8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;spNameQualifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://chat.example/saml/metadata&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;redeem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idJag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticatedClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;1.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Bind&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iss&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trusting&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;iss&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unverified_issuer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idJag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;invalid_grant&quot;&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Verify&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signature&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;against&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;specific&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;issuers&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JWKS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify_jwt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idJag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jwks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;jwks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;invalid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;invalid_grant&quot;&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;//&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Perform&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typ&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;oauth-id-jag+jwt&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aud&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;resource_authorization_server_url&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;authenticatedClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resolveSamlSubject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applyScopePolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;issueAccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resolveSamlSubject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;saml-nameid&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issuer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;samlIssuer&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sp_name_qualifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spNameQualifier&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup_user_by_saml_nameid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nameid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;subId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sp_name_qualifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;invalid_grant&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;issuing-the-access-token&quot;&gt;Issuing the access token&lt;/h3&gt;

&lt;p&gt;Once you resolve the user, issue an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; scoped according to your local policy. Below is an example of an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; returned after successfully validating the ID-JAG and resolving the user.&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;OK&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json;charset=UTF-8&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Cache-Control&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;no-store&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;token_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Bearer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;access_token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2YotnFZFEjr1zCsicMWpAA&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;expires_in&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;86400&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;chat:read chat:write&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;Do not issue a refresh token. If your authorization server issues a refresh token, the client has durable access to your resource server, and the IdP cannot revoke access.&lt;/p&gt;

  &lt;p&gt;The ID-JAG replaces the need for a refresh token. On access token expiry, the client resubmits the same ID-JAG to your token endpoint, and you mint a new access token against it. Only once the ID-JAG itself expires does the client request a new ID-JAG from the IdP using its own refresh token.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;updating-authorization-server-metadata&quot;&gt;Updating authorization server metadata&lt;/h3&gt;

&lt;p&gt;Clients locate your XAA support via your authorization server metadata (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/.well-known/oauth-authorization-server&lt;/code&gt;). Ensure you include the supported fields:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;issuer&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://chat.example&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;token_endpoint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://auth.chat.example/oauth2/v1/token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;grant_types_supported&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;urn:ietf:params:oauth:grant-type:jwt-bearer&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;authorization_grant_profiles_supported&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;urn:ietf:params:oauth:grant-profile:id-jag&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;making-cross-application-requests-from-your-saml-app-securely&quot;&gt;Making cross-application requests from your SAML app securely&lt;/h2&gt;

&lt;p&gt;With these five steps complete, your SAML application is configured for Cross App Access. Agents can now authorize requests against your API while maintaining your existing production federation, eliminating the need for protocol migration.&lt;/p&gt;

&lt;p&gt;You can now use Okta to make cross-application requests with your SAML app.&lt;/p&gt;

&lt;h2 id=&quot;configure-your-xaa-saml-app-in-okta&quot;&gt;Configure your XAA SAML App in Okta&lt;/h2&gt;

&lt;p&gt;Let’s test your SAML application in Okta. Before you begin, you’ll need some configuration values from the xaa.dev site.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;a href=&quot;https://xaa.dev&quot;&gt;https://xaa.dev&lt;/a&gt;.  Under the heading &lt;strong&gt;“Ready to bring your own actors?”&lt;/strong&gt; select the &lt;strong&gt;Resource App&lt;/strong&gt; option.  You will now select &lt;strong&gt;Test it against a hosted Requesting App&lt;/strong&gt; option. Now select the &lt;strong&gt;SAML&lt;/strong&gt; option. Finally, select the &lt;strong&gt;Take me there &amp;gt;&lt;/strong&gt; button.  You may be prompted for an email address. Enter any valid formatted email, then press continue. You’ll need two values: the &lt;strong&gt;Single Sign-On URL&lt;/strong&gt; (Assertion Consumer Service, or ACS) URL and the &lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Keep this site open in your browser; you’ll return to it throughout the setup.&lt;/p&gt;

&lt;h3 id=&quot;create-the-saml-20-resource-app-in-okta&quot;&gt;Create the SAML 2.0 resource app in Okta&lt;/h3&gt;

&lt;p&gt;Before you begin this step, you’ll need an Okta Integrator Free Plan account. &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Sign up for a new account&lt;/a&gt; to test out the XAA features.&lt;/p&gt;

&lt;p&gt;Cross App Access is an early access feature in Okta. New Integrator Free Plan account types include XAA support. If you have a paid Okta org plan and the following options are missing, contact your representative.&lt;/p&gt;

&lt;p&gt;Sign in to your Integrator Free Plan org and open the &lt;strong&gt;Admin Console&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Enable AI Agent Identity Assertion:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Settings &amp;gt; Features &amp;gt; Early Access&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Find &lt;strong&gt;AI Agent Identity Assertion&lt;/strong&gt; and &lt;strong&gt;Agent to Agent Connections&lt;/strong&gt;, and enable both&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll need an Okta application representing your requesting and resource apps.&lt;/p&gt;

&lt;p&gt;If you don’t have Okta SAML 2.0 applications representing your requesting and resource apps, you’ll need to create them. Create Okta SAML 2.0 applications by following these instructions.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Create App Integration&lt;/strong&gt;. In the &lt;strong&gt;Create a new app integration&lt;/strong&gt; modal, select &lt;strong&gt;SAML 2.0&lt;/strong&gt; and press &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: Enter a descriptive name for the app, for example,  “Resource App”&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Single sign-on URL&lt;/strong&gt;: Use the ACS URL of your resource app, e.g., “https://idp.xaa.dev/saml-requester/acs”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;: Use the SP Entity of your requestor or resource app, e.g., “https://idp.xaa.dev/saml-requester/metadata”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Name ID format&lt;/strong&gt;: select &lt;strong&gt;EmailAddress&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Application username&lt;/strong&gt;: select &lt;strong&gt;Email&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update application username on&lt;/strong&gt;: select &lt;strong&gt;Create and update&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Press &lt;strong&gt;Finish&lt;/strong&gt; to create the Okta SAML 2.0 application.&lt;/p&gt;

&lt;p&gt;After creating the app, you’ll see more configuration options for your Okta SAML 2.0 app. You’ll make changes in more than one tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign On configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Sign On&lt;/strong&gt; tab. Locate the Metadata URL field and press Copy to save it to your clipboard. Paste this URL to the SAML app metadata URL field in &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt; and save.&lt;/p&gt;

&lt;p&gt;Your SSO endpoint and Token endpoint are automatically configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assignments configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Assignments&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for your test user and select &lt;strong&gt;Assign&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Save and Go Back&lt;/strong&gt;, then select &lt;strong&gt;Done&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Resource Server extra configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Resource Server&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Enable XAA&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Issuer URL&lt;/strong&gt;: Use your resource authorization server issuer URL. This value becomes the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; claim in the ID-JAG and cannot change without deleting and resetting the connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;create-a-saml-20-requester-app-for-testing&quot;&gt;Create a SAML 2.0 requester app for testing&lt;/h3&gt;

&lt;p&gt;Return to &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt;. This is where you will provide these two values: the &lt;strong&gt;Single Sign-On URL&lt;/strong&gt; (the Assertion Consumer Service (ACS) URL) and the &lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create another Okta SAML 2.0 application in the Okta Admin screen by following these instructions.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Create App Integration&lt;/strong&gt;. In the &lt;strong&gt;Create a new app integration&lt;/strong&gt; modal, select &lt;strong&gt;SAML 2.0&lt;/strong&gt; and press &lt;strong&gt;Next&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: Enter a descriptive name for the app, for example,  “Requesting App for Testing”&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Single sign-on URL&lt;/strong&gt;: Use the ACS URL from xaa.dev, e.g., “https://idp.xaa.dev/saml-requester/acs”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;: Use the SP Entity from xaa.dev, e.g., “https://idp.xaa.dev/saml-requester/metadata”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Name ID format&lt;/strong&gt;: select &lt;strong&gt;EmailAddress&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Application username&lt;/strong&gt;: select &lt;strong&gt;Email&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Update application username on&lt;/strong&gt;: select &lt;strong&gt;Create and update&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Next&lt;/strong&gt; to continue&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Press &lt;strong&gt;Finish&lt;/strong&gt; to create the Okta SAML 2.0 application.&lt;/p&gt;

&lt;p&gt;After creating the app, you’ll see more configuration options for your Okta SAML 2.0 app. You’ll make changes in more than one tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign On configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;Sign On&lt;/strong&gt; tab. Locate the Metadata URL field and press Copy to save it to your clipboard. You’ll paste this URL to the SAML app metadata URL field in &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt; and save.&lt;/p&gt;

&lt;p&gt;Your SSO endpoint and Token endpoint are automatically configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assignments configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Assignments&lt;/strong&gt; tab and make the following configuration changes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for your test user and select &lt;strong&gt;Assign&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Save and Go Back&lt;/strong&gt;, then select &lt;strong&gt;Done&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;register-and-configure-the-ai-agent-in-okta&quot;&gt;Register and configure the AI Agent in Okta&lt;/h3&gt;

&lt;p&gt;With your Okta SAML 2.0 requesting app configured, register a new AI Agent in Okta. The AI Agent configuration represents the relationship between the Okta SAML 2.0 app you created and your MCP Resource Application. You’ll configure credentials, add your requesting app as a delegated caller, and connect your MCP resource app as a Resource Connection.&lt;/p&gt;

&lt;p&gt;In the Okta &lt;strong&gt;Admin Console&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Directory &amp;gt; AI Agents&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Register AI Agent &amp;gt; Register Manually&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enter a &lt;strong&gt;Name&lt;/strong&gt;, e.g., “Requesting Agent”&lt;/li&gt;
  &lt;li&gt;Select Register&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Select the AI Agent you just created to open its configuration. Configure the agent across the following tabs:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;On the &lt;strong&gt;Owners&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Assign individual owners&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Search for and add yourself as an owner&lt;/li&gt;
      &lt;li&gt;Press &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Credentials&lt;/strong&gt; tab (select on the &lt;strong&gt;Requesting Agent&lt;/strong&gt; to see this tab)
    &lt;ol&gt;
      &lt;li&gt;Copy the &lt;strong&gt;AI agent ID&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Return to the &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt; site to add the&lt;strong&gt;AI agent ID&lt;/strong&gt; value as the &lt;strong&gt;Client ID&lt;/strong&gt; and save&lt;/li&gt;
      &lt;li&gt;Back in Okta, select the &lt;strong&gt;Add Public Key&lt;/strong&gt;, and then press &lt;strong&gt;Generate new key&lt;/strong&gt;. Okta generates a key pair and displays the private key. Under &lt;strong&gt;PEM&lt;/strong&gt;, press &lt;strong&gt;Copy to clipboard&lt;/strong&gt; and store the key safely. You’ll paste this private key into the &lt;strong&gt;Private key (PKCS8 PEM or private JWK)&lt;/strong&gt; field in &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt;.&lt;/li&gt;
      &lt;li&gt;Copy and paste the associated &lt;strong&gt;KEY ID&lt;/strong&gt; value and paste it into the &lt;strong&gt;kid&lt;/strong&gt; field at  &lt;a href=&quot;https://xaa.dev/developer/test-resource?tab=saml&quot;&gt;xaa.dev&lt;/a&gt; and save.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Back at Okta, on the &lt;strong&gt;Delegations&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Caller&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Search for the newly created Okta SAML requesting app&lt;/li&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Caller&lt;/strong&gt; to confirm&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Resource Connections&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;Select &lt;strong&gt;Add Resource Connection&lt;/strong&gt;. Under the &lt;strong&gt;Resource&lt;/strong&gt; section, select &lt;strong&gt;Application&lt;/strong&gt; as the &lt;strong&gt;resource type&lt;/strong&gt;.
        &lt;ol&gt;
          &lt;li&gt;Under the &lt;strong&gt;Application&lt;/strong&gt; section, choose your &lt;strong&gt;Application&lt;/strong&gt; instance – MCP (Resource App) – from the dropdown menu and paste the &lt;strong&gt;Client ID&lt;/strong&gt; at the &lt;strong&gt;Resource Authorization Server&lt;/strong&gt;.&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;Under &lt;strong&gt;Scope Condition&lt;/strong&gt;, select &lt;strong&gt;Allow all&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Activate the agent
    &lt;ol&gt;
      &lt;li&gt;The final step is to activate the AI agent, Go to Actions and select &lt;strong&gt;Activate&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the AI Agent is active, the configuration is complete. All checkmarks on the agent configuration page must be green.&lt;/p&gt;

&lt;p&gt;Confirm the AI Agent configuration is complete. All checkmarks on the agent configuration page must be green.&lt;/p&gt;

&lt;h2 id=&quot;verify-your-okta-xaa-setup-on-xaadev&quot;&gt;Verify your Okta XAA setup on xaa.dev&lt;/h2&gt;

&lt;p&gt;Before we get to the next section, make sure you have the resource app URL in the resource authorization issuer (ID-JAG audience).  By this point, you’ll have every value from the checklist and your one-time Okta setup in place (AI Agent, credentials, owner, delegation, and resource connection), so we’ll add the values from Okta and the apps to walk through the flow step by step, one button per step.&lt;/p&gt;

&lt;p&gt;The screenshot below shows the SAML configuration values step on xaa.dev.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/xaadev-sso-e88a55e57ea2013d843abf011c4ea15ed2f86320a141ff5590156265489ae483.jpg&quot; alt=&quot;Register and test a SAML resource app form values to establish a SAML client.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;configure-saml-sso&quot;&gt;Configure SAML SSO&lt;/h3&gt;

&lt;p&gt;Press &lt;strong&gt;Start SAML login at your IdP&lt;/strong&gt; and complete the login in the pop-up.&lt;/p&gt;

&lt;p&gt;When it closes, the step turns green and shows a &lt;strong&gt;✓ Auto-discovered SSO&lt;/strong&gt; endpoint, confirming that the tester resolved the real &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.../sso/saml&lt;/code&gt; endpoint from your metadata and returned a signed SAML assertion.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/xaadev-saml-sso-response-e85f7a897abc9bb1ed9dccd52ae40345911c9e3212b7218ee622f2b79011eecd.jpg&quot; alt=&quot;SAML SSO code request to initiate login through your IdP.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;confirm-the-saml-assertion-exchange-for-a-refresh-token&quot;&gt;Confirm the SAML Assertion exchange for a refresh token&lt;/h3&gt;

&lt;p&gt;Press &lt;strong&gt;Exchange assertion for refresh token&lt;/strong&gt;. The tester posts the signed assertion to your IdP’s token endpoint, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;private_key_jwt&lt;/code&gt; authentication. A 200 means the identity provider accepted the assertion, and you now hold an opaque refresh token.&lt;/p&gt;

&lt;h3 id=&quot;verify-the-refresh-token-exchange-for-an-id-jag-token&quot;&gt;Verify the refresh token exchange for an ID-JAG token&lt;/h3&gt;

&lt;p&gt;Press &lt;strong&gt;Exchange refresh token for ID-JAG&lt;/strong&gt;. This action returns a decoded ID-JAG. Take a second to review it: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; should equal your Resource authorization issuer, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt; should contain the SAML NameID of the user who logged in. The Resource authorization server then validates this token. A 200 OK indicates that the step succeeded.&lt;/p&gt;

&lt;h3 id=&quot;redeem-the-id-jag-for-an-access-token-at-the-resource-authorization-server&quot;&gt;Redeem the ID-JAG for an access token at the resource authorization server&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Fill in your Resource AS token endpoint&lt;/li&gt;
  &lt;li&gt;Client ID and client secret of the resource app from the Resource Authorization Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Press &lt;strong&gt;Redeem&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grant_type=jwt-bearer&lt;/code&gt;). If the request succeeds, you’ll receive a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;200 OK&lt;/code&gt; response with an access token. Inspect the token in the &lt;strong&gt;Token&lt;/strong&gt; tab to verify that the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scope&lt;/code&gt; claims match the values configured in your resource authorization server. This validation confirms that the authorization server accepted the ID-JAG and issued its own access token.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/redeem-id-jag-8042660d2adc24648d1d2ddf5c6f8bd5b44e94da74263748e38309590bfc58de.jpg&quot; alt=&quot;Redeem-ID-JAG at your Resource Authorization Server screen, showing a successful execution with a 200 OK code.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;call-the-resource-api-with-the-access-token&quot;&gt;Call the resource API with the access token&lt;/h3&gt;

&lt;p&gt;Select the request method and enter your API URL (The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization: Bearer&lt;/code&gt; header is added automatically, but you can add any other headers or a request body as needed), then press &lt;strong&gt;Send GET Request&lt;/strong&gt;. A 200 response from your endpoint is the final proof: your API accepts the access token generated by the ID-JAG exchange.&lt;/p&gt;

&lt;h3 id=&quot;prove-the-xaa-connection-end-to-end&quot;&gt;Prove the XAA connection end-to-end&lt;/h3&gt;

&lt;p&gt;A green &lt;strong&gt;Conformance passed&lt;/strong&gt; panel appears. Select &lt;strong&gt;Export conformance log (JSON)&lt;/strong&gt; to download the test results. The export includes the signed ID-JAG, the access token returned by your resource authorization server, and the API response.&lt;/p&gt;

&lt;p&gt;You can share this file with your IdP as proof that the Cross App Access integration works successfully.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/cross-app-access-saml/xaadev-conformance-97204405c2e72d134fec8e2ac9cca894236bcb2142508a3f8457814baf323967.jpg&quot; alt=&quot;Conformance passed. Export your proof. A button allows exporting a conformance log in JSON format.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;takeaways-for-implementors-who-have-both-oidc-and-saml-apps&quot;&gt;Takeaways for implementors who have both OIDC and SAML apps&lt;/h2&gt;

&lt;p&gt;If you have already implemented XAA in your OIDC apps, here’s a quick checklist to convert your SAML apps:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The subject comes from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;saml-nameid&lt;/code&gt; format, rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Match on every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;saml-nameid&lt;/code&gt; member (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;issuer&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NameID&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sp_name_qualifier&lt;/code&gt;), rather than just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Everything else, including token issuance rules and redemption checks, remains as is&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;learn-more-about-cross-app-access-saml-and-oauth-20&quot;&gt;Learn more about Cross App Access, SAML, and OAuth 2.0&lt;/h2&gt;

&lt;p&gt;If this guide helped you implement Cross App Access with SAML, explore these resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;📘 &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/apps/apps-cross-app-access.htm&quot;&gt;Cross App Access Documentation&lt;/a&gt;: Official guides for configuring and managing Cross App Access in production.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;🎙️ &lt;a href=&quot;https://www.youtube.com/watch?v=qKs4k5Y1x_s&quot;&gt;Developer Podcast on MCP and Cross App Access&lt;/a&gt;: Hear the backstory, use cases, and why this matters for developers.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;📋 &lt;a href=&quot;/blog/2026/07/06/submit-oin-xaa&quot;&gt;How to Build and List Secure Cross App Access (XAA) Connections on Okta Integration Network (OIN)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Identity 101:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/identity-101/whats-the-difference-between-oauth-openid-connect-and-saml/&quot;&gt;What’s the Difference Between OAuth, OpenID Connect, and SAML?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/en-in/identity-101/saml-vs-oauth/&quot;&gt;What are SAML, OAuth, and OIDC?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/identity-101/why-you-should-migrate-to-oauth-2-0-from-static-api-tokens/&quot;&gt;Why You Should Migrate to OAuth 2.0 From Static API Tokens&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2023/07/27/enterprise-ready-getting-started&quot;&gt;How to Get Going with the On-Demand SaaS Apps Workshops&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow us on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt;, and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube&lt;/a&gt; channel. Leave a comment below if you have any questions!&lt;/p&gt;
</description>
        <pubDate>Fri, 03 Jul 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/07/03/cross-app-access-saml</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/07/03/cross-app-access-saml</guid>
      </item>
    
      <item>
        <title>The Builder Revolution: Why We're Shifting to Builder Advocacy</title>
        <description>&lt;p&gt;My team has officially changed its name, and it’s one I’ve been excited about for a while.&lt;/p&gt;

&lt;p&gt;My team is now Builder Advocacy, and our organization is now Builder Experience (BX). We were previously known as Developer Advocacy and Developer Success, respectively.&lt;/p&gt;

&lt;p&gt;On the surface, it might look like a simple rebranding. But it’s not. It’s a recognition of something that’s been happening in our industry for a while and has accelerated dramatically recently with this understanding: the definition of who builds things with technology has fundamentally changed.&lt;/p&gt;

&lt;h2 id=&quot;the-builder-revolution-is-already-here&quot;&gt;The builder revolution is already here&lt;/h2&gt;

&lt;p&gt;For most of computing history, “developer” meant someone who wrote code; they were someone who knew a language, understood a framework, and could navigate a terminal.&lt;/p&gt;

&lt;p&gt;That world has changed.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href=&quot;https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/&quot;&gt;GitHub Octoverse 2025 report&lt;/a&gt;, GitHub now has over 180 million developers, with more than one new developer joining every single second. In 2025 alone, 36 million new developers joined the platform, a 23% year-over-year increase. There are now 4.3 million AI-related repositories on GitHub, nearly double the number in 2023, and monthly contributions to AI projects reached approximately 6 million in August 2025, up 188% year over year. Nearly 80% of new developers on GitHub use GitHub Copilot within their first week.&lt;/p&gt;

&lt;p&gt;Meanwhile, the &lt;a href=&quot;https://survey.stackoverflow.co/2025/&quot;&gt;Stack Overflow Developer Survey 2025&lt;/a&gt; found that 84% of respondents are currently using or plan to use AI tools in their development work, up from 76% just a year earlier. Nearly half of all developers now use AI tools daily.&lt;/p&gt;

&lt;p&gt;AI is already part of the workflow for the majority of people building technology.&lt;/p&gt;

&lt;p&gt;But here’s the part that really matters: AI hasn’t just made developers more productive. It has dramatically expanded who can build in the first place.&lt;/p&gt;

&lt;h2 id=&quot;a-new-kind-of-builder&quot;&gt;A new kind of builder&lt;/h2&gt;

&lt;p&gt;Think about who else is building today, beyond traditional developers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The product manager who spins up an AI agent to automate their team’s weekly reporting.&lt;/li&gt;
  &lt;li&gt;The marketing operations analyst who connects APIs through a workflow tool like Zapier without writing a single line of code.&lt;/li&gt;
  &lt;li&gt;The IT admin who builds a customer-facing chatbot using a low-code platform in an afternoon.&lt;/li&gt;
  &lt;li&gt;The entrepreneur who vibe-codes their MVP with the help of a large language model (LLM), deploying something that would have taken a full engineering team six months just five years ago.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These people are builders. They may not call themselves developers. They may not have a degree in computer science or formal education in software. But they are creating real, production-level experiences that real users depend on, and they deserve the same quality of advocacy and support that we’ve historically directed at developers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developers.hubspot.com/blog&quot;&gt;HubSpot&lt;/a&gt; has begun distinguishing “builders” as a specific user persona: those who manage automations, middleware, and integrations, recognizing that the people building on their platform aren’t always traditional developers. &lt;a href=&quot;https://reinvent.awsevents.com/&quot;&gt;AWS&lt;/a&gt; has long referred to its community as “builders,” a term that appears throughout their re:Invent programming, documentation, and community initiatives. &lt;a href=&quot;https://trailhead.salesforce.com/trailblazer-community&quot;&gt;Salesforce’s legendary Trailblazer community&lt;/a&gt; has expanded to embrace AI builders alongside traditional admins and developers as agentic AI becomes central to their platform. Google took this a step further in April 2026 by &lt;a href=&quot;https://cloud.google.com/blog/topics/developers-practitioners/introducing-the-builders-hub-from-the-google-developer-program&quot;&gt;launching the Builders Hub&lt;/a&gt; through their Developer Program, even moving the URL to &lt;a href=&quot;https://builders.google&quot;&gt;builders.google&lt;/a&gt;. Google is explicitly designed for “vibe coders, AI builders, and professional developers” alike. These are not coincidences. They are signals of a broader industry shift in how we think about the people who build with our platforms.&lt;/p&gt;

&lt;h2 id=&quot;what-builder-advocacy-means&quot;&gt;What builder advocacy means&lt;/h2&gt;

&lt;p&gt;With this name change, my team is committing — a commitment to showing up for all three types of builders we serve:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Developers: traditional coders, engineers, and architects who have always been our core audience. They’re not going anywhere, and neither is our dedication to them.&lt;/li&gt;
  &lt;li&gt;AI Builders: prompt engineers, LLM integrators, AI agent creators, and machine learning (ML) practitioners who are building the next generation of intelligent systems. This community is growing at a pace we’ve never seen before.&lt;/li&gt;
  &lt;li&gt;Automators: no-code and low-code builders who connect systems, design workflows, and create value without traditional programming. They are increasingly building mission-critical infrastructure, and they deserve advocacy, documentation, and tooling that treats them as first-class citizens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these name changes — from Developer Success to Builder Experience (BX), and from Developer Advocacy to Builder Advocacy — our team is saying clearly: We see all of you. We are here for all of you.&lt;/p&gt;

&lt;h2 id=&quot;ai-made-its-impact-its-not-going-away&quot;&gt;AI made its impact. It’s not going away&lt;/h2&gt;

&lt;p&gt;Some name changes are cosmetic. This one isn’t.&lt;/p&gt;

&lt;p&gt;The community building with AI isn’t concentrated in a few zip codes anymore. It is global, diverse, and growing in ways that don’t fit neatly into the old categories.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://survey.stackoverflow.co/2025/&quot;&gt;Stack Overflow Developer Survey 2025&lt;/a&gt; found that 84% of developers are now using or planning to use AI tools. Among those using AI agents, roughly 70% say agents boosted their productivity and reduced task time. The tools are proving their value in the workflow, not just in the demo.&lt;/p&gt;

&lt;p&gt;That’s the community I want to be part of. That’s the community I want to advocate for.&lt;/p&gt;

&lt;h2 id=&quot;whats-next-for-builders&quot;&gt;What’s next for builders&lt;/h2&gt;

&lt;p&gt;This name change is effective today, but it’s really the beginning of a larger journey. Our Builder Experience organization is investing in resources, content, and community programs that serve the full spectrum of builders: from the seasoned backend engineer to the AI tinkerer to the automation-first operator.&lt;/p&gt;

&lt;p&gt;And here’s what hasn’t changed. You can still find us where you always have:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For documentation, visit our &lt;a href=&quot;https://developer.okta.com/docs/&quot;&gt;developer docs&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;For support, head to our &lt;a href=&quot;https://devforum.okta.com/&quot;&gt;developer forum&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;For helpful videos, visit our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube channel&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;On social media, follow us on our &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn page&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build — whatever that means to you — we’re here for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Welcome to Builder Advocacy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://x.com/oktadev&quot;&gt;X&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube channel&lt;/a&gt; for more exciting content. We also want to hear from you about the topics you’d like to see and any questions you may have. Leave us a comment below!&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Jun 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/06/30/builder-revolution</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/06/30/builder-revolution</guid>
      </item>
    
      <item>
        <title>How Verifiable Digital Credentials Are Reshaping Trust Architecture</title>
        <description>&lt;p&gt;The identity stack is getting a new layer, and it’s already in your users’ pockets. Mobile driver’s licenses are live in dozens of U.S. states. The EU is mandating digital identity wallets for hundreds of millions of citizens. Apple Wallet and Google Wallet already hold government-issued credentials. Yet most development teams are not building for it.&lt;/p&gt;

&lt;p&gt;The layer is called Verifiable Digital Credentials. The issuer infrastructure is already live, with wallets deployed and standards stable. The industry’s verifier-side adoption is still catching up, and that’s where we’re focusing our engineering work right now.&lt;/p&gt;

&lt;p&gt;For years, when applications needed to verify a user’s age, license, or employment status, the default methods were document uploads, third-party verification APIs, and centralized storage of personally identifiable information (PII). It worked. But it accumulated cost at every step: data your infrastructure had to protect, compliance obligations that grew with every sensitive record, manual review queues that created friction, and third-party vendors holding your users’ identity data on your behalf.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/verifiable-digital-credentials/image0-cdff3ac0d51b5503ad25f9bc7f02465a951b98b7c780741b3d65db2c2d5f1a83.jpg&quot; width=&quot;800&quot; alt=&quot;blog/verifiable-digital-credentials/image0.jpg&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;VDCs replace that model. A trusted authority, such as a DMV, a university, or an employer, issues a cryptographically signed credential. The user holds it in a wallet. Your application requests the specific proof it needs. The credential validates itself. You record the outcome and move on. No document stored. No PII beyond what the transaction required. No third party in the middle.&lt;/p&gt;

&lt;p&gt;The technical foundation relies on the &lt;strong&gt;Issuer-Holder-Verifier trust triangle&lt;/strong&gt;: the Issuer signs the credential, the Holder stores it in a digital wallet, and the Verifier, your application, requests exactly the proof it needs. This model enables selective disclosure, where a verifier can confirm a person meets an age threshold without ever seeing their date of birth, home address, or a photo of their physical ID. By moving from document collection to claim validation, you eliminate manual review queues and dramatically reduce the personal data your infrastructure is obligated to protect.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/verifiable-digital-credentials/image1-52a8315e3f336adca9499d8c13f8c5dd9980017402471b70033d273be682e9ce.jpg&quot; width=&quot;800&quot; alt=&quot;blog/verifiable-digital-credentials/image1.jpg&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is not an incremental improvement on document uploads. It is a different architecture for how applications establish trust, one in which data collection is scoped to exactly what the transaction requires, and verification outcomes are separable from the identity artifacts that produced them.&lt;/p&gt;

&lt;p&gt;The timing matters. The standards are mature. The wallets are deployed at scale. The trust ecosystems are live. What the ecosystem needs now is verifier-side applications that implement these flows well. That gap, between where the issuer infrastructure is and where verifier-side development is, is the opportunity in front of engineering teams right now.&lt;/p&gt;

&lt;p&gt;&lt;strong class=&quot;hide&quot;&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#authentication-proves-identity-credentials-prove-claims&quot; id=&quot;markdown-toc-authentication-proves-identity-credentials-prove-claims&quot;&gt;Authentication proves identity. Credentials prove claims.&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-vdc-standards-stack-what-you-need-to-know-before-you-build&quot; id=&quot;markdown-toc-the-vdc-standards-stack-what-you-need-to-know-before-you-build&quot;&gt;The VDC standards stack: What you need to know before you build&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#verification-is-not-just-a-valid-signature&quot; id=&quot;markdown-toc-verification-is-not-just-a-valid-signature&quot;&gt;Verification is not just a valid signature&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#progressing-beyond-the-digital-credentials-api-to-production-vdc-verification&quot; id=&quot;markdown-toc-progressing-beyond-the-digital-credentials-api-to-production-vdc-verification&quot;&gt;Progressing beyond the Digital Credentials API to production VDC verification&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-verifiable-digital-credentials&quot; id=&quot;markdown-toc-learn-more-about-verifiable-digital-credentials&quot;&gt;Learn more about Verifiable Digital Credentials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;authentication-proves-identity-credentials-prove-claims&quot;&gt;Authentication proves identity. Credentials prove claims.&lt;/h2&gt;

&lt;p&gt;The most important distinction in the VDC space is also the most commonly missed.&lt;/p&gt;

&lt;p&gt;Authentication answers one question: is this user the legitimate owner of this account? Verification answers a completely different question: can this person prove a specific attribute about themselves, their age, their license, their employment status, or their eligibility?&lt;/p&gt;

&lt;p&gt;These are not the same problem. We have spent the last decade perfecting the art of logging users in (OpenID Connect, passkeys, strong MFA), but stretching your auth layer to carry verification responsibilities it was never designed for produces login flows that collect far more than they need to, and systems that are harder to evolve as both concerns grow independently.&lt;/p&gt;

&lt;p&gt;The right architecture keeps them separate: authentication establishes the session, and VDC-based verification handles moments when a higher-trust signal is required.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/verifiable-digital-credentials/image2-4226eb7a4fd9b7616dd62b3dfae88ee9226072e0220a04ab2fb65083b40abef3.jpg&quot; width=&quot;800&quot; alt=&quot;blog/verifiable-digital-credentials/image2.jpg&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In practice, those moments cluster around a recognizable set of product checkpoints: age gating for regulated goods, step-up verification before sensitive account actions, professional license checks, workforce credential validation, and high-value transactions. These are the natural insertion points for VDC-based verification. Not a replacement for your auth stack, but an additional layer, deployed precisely where the cost of a trust failure is highest.&lt;/p&gt;

&lt;h2 id=&quot;the-vdc-standards-stack-what-you-need-to-know-before-you-build&quot;&gt;The VDC standards stack: What you need to know before you build&lt;/h2&gt;

&lt;p&gt;VDCs are not a single specification. They are a layered ecosystem of interoperable standards, and understanding each layer is foundational to making good architectural decisions. The stack has three layers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/verifiable-digital-credentials/image3-57253503a0f142a443cfe5fc984d10b13cda28b01a905ed6587905e6f87b458a.jpg&quot; width=&quot;800&quot; alt=&quot;blog/verifiable-digital-credentials/image3.jpg&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential Formats&lt;/strong&gt; - defines what the credential is and how selective disclosure works. Two formats matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SD-JWT VC&lt;/strong&gt; - selective disclosure built on JSON Web Tokens (JWTs) and developed in the IETF. This is the natural entry point for web and backend teams already working with JWT-based auth. The claims structure is familiar, and selective disclosure is native to the format. The right choice for enterprise credentials, age gating, employment and license verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mdoc (ISO 18013-5/7)&lt;/strong&gt; - the format behind mobile driver’s licenses and government-issued digital IDs. A more tightly governed interoperability environment, stricter format expectations, and more prescribed wallet behavior. If your use case touches government-issued identity, this is your lane.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protocols&lt;/strong&gt; - define how credentials move between issuers, wallets, and verifiers. Two specs from the OpenID for Verifiable Credentials family handle both sides of the lifecycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OpenID4VCI&lt;/strong&gt; governs issuance - how a wallet obtains a credential from an issuer. The issuer-side handshake. &lt;strong&gt;OpenID4VP&lt;/strong&gt; governs presentation - how a wallet delivers proof of a claim to your application. It is credential-format-agnostic, supports redirect-based flows today, and supports the Digital Credentials API for web-native UX, where supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The W3C Digital Credentials API&lt;/strong&gt; - the runtime layer that makes this usable in a browser. Its role is directly analogous to what WebAuthn does for passkeys: it standardizes how browsers invoke credential wallets, provides users proper consent context, and eliminates the custom URL scheme and deep-link hacks that make wallet invocation brittle today.&lt;/p&gt;

&lt;p&gt;In simple terms: the &lt;strong&gt;Digital Credentials API invokes the wallet&lt;/strong&gt;, &lt;strong&gt;OpenID4VCI and OpenID4VP manage the protocol flow&lt;/strong&gt;, and &lt;strong&gt;SD-JWT VC or mdoc define the credential format&lt;/strong&gt;. These layers are complementary, not interchangeable.&lt;/p&gt;

&lt;h2 id=&quot;verification-is-not-just-a-valid-signature&quot;&gt;Verification is not just a valid signature&lt;/h2&gt;

&lt;p&gt;Verifying digital credentials requires more than cryptographic signature validation. A credential can have a mathematically valid signature and still be unacceptable in your application’s context. What determines acceptability is ecosystem trust.&lt;/p&gt;

&lt;p&gt;Two concepts are non-negotiable in any verifier-side implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust List&lt;/strong&gt; - the curated set of issuers and signing keys your verifier accepts as authoritative. Think of it as conceptually equivalent to a browser’s root CA store. If the issuer is not on your trust list, the credential is invalid in your context regardless of signature validity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust Framework&lt;/strong&gt; - the policy and governance rules defining who can participate in the ecosystem and under what conditions. This is the legal and business contract behind the cryptography.&lt;/p&gt;

&lt;p&gt;In the U.S. mDL ecosystem, for example, state DMVs publish their signing keys through the AAMVA Digital Trust Service, which serves as a trust anchor for Issuing Authority Certificate Authorities (IACAs). Your verification flow must integrate with that trust source and handle key rotation as issuers update their infrastructure. Build the trust framework integration as a first-class architectural requirement during implementation.&lt;/p&gt;

&lt;h2 id=&quot;progressing-beyond-the-digital-credentials-api-to-production-vdc-verification&quot;&gt;Progressing beyond the Digital Credentials API to production VDC verification&lt;/h2&gt;

&lt;p&gt;The browser rollout of native APIs is still progressing. Developers should not architect a flow that requires the Digital Credentials API as a hard dependency today. The most resilient path is to build on redirect-based OpenID4VP as your baseline; it works universally without specific browser dependencies, and then layer in the Digital Credentials API as a progressive enhancement to provide a first-class UX where supported.&lt;/p&gt;

&lt;p&gt;The broader trends driving VDC adoption are not speculative. Government credential infrastructure is becoming a dependency layer on which developers can build. Data minimization is shifting from best practice to legal requirement as privacy regulations tighten globally. The auth and verification layers are separating permanently as both mature independently. Managed verification platforms are closing the tooling gap by abstracting away format handling, protocol flows, and trust-source integrations, so teams do not have to build every layer from scratch.&lt;/p&gt;

&lt;p&gt;The practical path for engineering teams is incremental. Start with one verification moment in your product where the current approach creates measurable friction or accumulates unnecessary data liability. Define the minimum claim that moment actually requires. Keep it separate from your auth layer. Build something bounded. Learn from it. Expand.&lt;/p&gt;

&lt;p&gt;The identity stack is getting a new layer. The developers who understand the formats, protocols, trust model, and browser reality are the ones who will architect what comes next. That window is open now, and the gap between where issuer infrastructure is and where verifier-side development is leaves real ground to gain for teams that move early.&lt;/p&gt;

&lt;p&gt;The question for every senior engineer reading this is not whether VDCs are coming. They are already here. The question is whether your architecture is ready to meet them. &lt;a href=&quot;https://oktacredentials.dev/&quot;&gt;Explore the VDC platform beta today&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-verifiable-digital-credentials&quot;&gt;Learn more about Verifiable Digital Credentials&lt;/h2&gt;

&lt;p&gt;If you’d like to explore the standards and ecosystem behind verifiable digital credentials in more detail, these resources are a good starting point.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/identity-101/what-are-verifiable-digital-credentials/&quot;&gt;What Are Verifiable Digital Credentials?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://oktacredentials.dev/&quot;&gt;Getting Started VDC Guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://openid.net/sg/openid4vc/&quot;&gt;OpenID for Verifiable Credentials - Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.w3.org/TR/digital-credentials/&quot;&gt;W3C Digital Credentials API&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow us on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt;, and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/oktadev&quot;&gt;YouTube&lt;/a&gt; channel to see more content like this. If you have any questions, please comment below!&lt;/p&gt;
</description>
        <pubDate>Mon, 29 Jun 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/06/29/verifiable-digital-credentials</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/06/29/verifiable-digital-credentials</guid>
      </item>
    
  </channel>
</rss>
