<?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>Supercharge Auth with Signals and the New Okta Angular SDK</title>
        <description>&lt;p&gt;Have you noticed that the Okta Angular SDK went fully standalone? There’s no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NgModule&lt;/code&gt; left to import, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;importProvidersFrom&lt;/code&gt; wrapper to remember, and the guards are plain functions now. If you’ve been waiting for the SDK to look like the rest of your standalone Angular app, this is the release you were waiting for.&lt;/p&gt;

&lt;p&gt;In this post, we’ll pick up a small Angular v22 project and finish it. We’ll make the following changes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Add authentication using the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideOktaAuth&lt;/code&gt; provider function&lt;/li&gt;
  &lt;li&gt;Protect a route with the SDK’s functional guard&lt;/li&gt;
  &lt;li&gt;Load each user’s groups with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; and a signal input&lt;/li&gt;
  &lt;li&gt;Move the Okta configuration to runtime loading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re calling Okta’s APIs directly for this project, so we don’t have to stand up a separate backend. If you want to jump to the completed project, you can find it in the &lt;a href=&quot;https://github.com/oktadev/okta-angular-auth-signals-example&quot;&gt;okta-angular-auth-signals-example&lt;/a&gt; GitHub repository. Otherwise, warm up your fingers and let’s get coding!&lt;/p&gt;

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

  &lt;p&gt;This post is best for developers familiar with Angular. If you are an Angular newbie, start by building &lt;a href=&quot;https://angular.dev/tutorials/learn-angular&quot;&gt;your first Angular app&lt;/a&gt; using the tutorial created by the Angular team.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;For this tutorial, you will need the following tools:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; v22 or greater&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://angular.dev/tools/cli&quot;&gt;Angular CLI&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;An &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Okta Integrator Free Plan account&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A web browser with good debugging capabilities&lt;/li&gt;
  &lt;li&gt;Your favorite IDE&lt;/li&gt;
  &lt;li&gt;Terminal window (if you aren’t using an IDE with a built-in terminal)&lt;/li&gt;
&lt;/ul&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;#get-the-starting-angular-project&quot; id=&quot;markdown-toc-get-the-starting-angular-project&quot;&gt;Get the starting Angular project&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#secure-the-angular-app-with-oauth-21-and-openid-connect-oidc-using-okta&quot; id=&quot;markdown-toc-secure-the-angular-app-with-oauth-21-and-openid-connect-oidc-using-okta&quot;&gt;Secure the Angular app with OAuth 2.1 and OpenID Connect (OIDC) using Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#grant-api-scopes-to-read-users-and-groups&quot; id=&quot;markdown-toc-grant-api-scopes-to-read-users-and-groups&quot;&gt;Grant API scopes to read users and groups&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#allow-cross-origin-requests-and-protect-the-access-token&quot; id=&quot;markdown-toc-allow-cross-origin-requests-and-protect-the-access-token&quot;&gt;Allow cross-origin requests and protect the access token&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#add-authentication-with-the-standalone-okta-angular-provider&quot; id=&quot;markdown-toc-add-authentication-with-the-standalone-okta-angular-provider&quot;&gt;Add authentication with the standalone Okta Angular provider&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#protect-routes-with-functional-route-guards&quot; id=&quot;markdown-toc-protect-routes-with-functional-route-guards&quot;&gt;Protect routes with functional route guards&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#add-users-to-your-okta-org&quot; id=&quot;markdown-toc-add-users-to-your-okta-org&quot;&gt;Add users to your Okta org&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#display-users-and-review-angulars-rxresource-api&quot; id=&quot;markdown-toc-display-users-and-review-angulars-rxresource-api&quot;&gt;Display users and review Angular’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#load-a-users-groups-with-rxresource-and-a-signal-input&quot; id=&quot;markdown-toc-load-a-users-groups-with-rxresource-and-a-signal-input&quot;&gt;Load a user’s groups with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; and a signal input&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#display-group-details-using-signal-inputs&quot; id=&quot;markdown-toc-display-group-details-using-signal-inputs&quot;&gt;Display group details using signal inputs&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#load-your-okta-configuration-at-runtime&quot; id=&quot;markdown-toc-load-your-okta-configuration-at-runtime&quot;&gt;Load your Okta configuration at runtime&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-angular-signals-standalone-apis-and-oidc&quot; id=&quot;markdown-toc-learn-more-about-angular-signals-standalone-apis-and-oidc&quot;&gt;Learn more about Angular signals, standalone APIs, and OIDC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;get-the-starting-angular-project&quot;&gt;Get the starting Angular project&lt;/h2&gt;

&lt;p&gt;We’re starting from a project instead of building everything from scratch, because a pile of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ng generate&lt;/code&gt; commands would distract us from the exciting coding parts. Open a terminal window and run the following commands to get a local copy of the starter and install dependencies. Feel free to fork the repo so you can track your changes.&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 &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; starter https://github.com/oktadev/okta-angular-auth-signals-example.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;okta-angular-auth-signals-example
npm ci
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the project in your IDE and take a look around. I already added &lt;a href=&quot;https://www.npmjs.com/package/@okta/okta-angular&quot;&gt;Okta Angular&lt;/a&gt; and &lt;a href=&quot;https://www.npmjs.com/package/@okta/okta-auth-js&quot;&gt;Okta Auth JS&lt;/a&gt; to the project, along with a few pieces you’d otherwise spend the afternoon typing:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/okta.ts&lt;/code&gt; is a service that calls Okta’s user management API. It uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@Service()&lt;/code&gt; decorator to declare a singleton service.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/okta.types.ts&lt;/code&gt; holds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaUser&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaGroup&lt;/code&gt; interfaces&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/okta-auth.interceptor.ts&lt;/code&gt; attaches the access token and a proof of possession to Okta requests, and only to Okta requests&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/app.routes.ts&lt;/code&gt; has the routes, including the SDK’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaCallbackComponent&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;login/callback&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t run the app yet. It won’t start, and that’s on purpose – there’s no authentication configuration in the project. The service and the interceptor both ask Angular for an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaAuth&lt;/code&gt; instance, and nothing provides one yet. That’s the first thing you’ll fix.&lt;/p&gt;

&lt;h2 id=&quot;secure-the-angular-app-with-oauth-21-and-openid-connect-oidc-using-okta&quot;&gt;Secure the Angular app with OAuth 2.1 and OpenID Connect (OIDC) using Okta&lt;/h2&gt;

&lt;p&gt;You’ll use Okta to handle authentication and authorization for your Angular application securely.&lt;/p&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 and Resources&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;In the &lt;strong&gt;Sign in Method&lt;/strong&gt; section, select &lt;strong&gt;OIDC - OpenID Connect&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Single-Page 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;In the &lt;strong&gt;Grant type&lt;/strong&gt; section, ensure that both &lt;strong&gt;Authorization Code&lt;/strong&gt; and &lt;strong&gt;Refresh Token&lt;/strong&gt; are selected&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:4200/login/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:4200&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;In the &lt;strong&gt;Assignments&lt;/strong&gt; section, assign a group or keep the default &lt;strong&gt;Everyone&lt;/strong&gt; group assigned for all&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 Single-Page App manually in the Admin Console configures your Okta Org with the application settings.  You may also need to configure trusted origins for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:4200&lt;/code&gt; in &lt;strong&gt;Security&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt; &amp;gt; &lt;strong&gt;Trusted Origins&lt;/strong&gt;.&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;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;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Issuer:    https://dev-133337.okta.com/oauth2/default
Client ID: 0oab8eb55Kb9jdMIr5d6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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-spa/-/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;p&gt;Note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Client ID&lt;/code&gt; and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Issuer&lt;/code&gt;. You’ll need those values in a moment.&lt;/p&gt;

&lt;p&gt;One important detail about that issuer. The credentials example above shows an issuer ending in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/oauth2/default&lt;/code&gt;, which is the custom authorization server Okta creates for you. This project uses the &lt;strong&gt;org&lt;/strong&gt; authorization server instead, so your issuer is your Okta domain with nothing after it, like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;<span class="okta-preview-domain">https://{yourOktaDomain}</span>&lt;/code&gt;. We need the org authorization server because it issues access tokens that the Okta management APIs accept, and reading users and groups is the entire point of this app.&lt;/p&gt;

&lt;p&gt;In your own applications, you’ll most likely use a custom authorization server for your APIs, since that’s where you control scopes, claims, and access policies. Read &lt;a href=&quot;https://developer.okta.com/docs/concepts/auth-servers/&quot;&gt;Authorization servers&lt;/a&gt; to understand which one fits your case.&lt;/p&gt;

&lt;h3 id=&quot;grant-api-scopes-to-read-users-and-groups&quot;&gt;Grant API scopes to read users and groups&lt;/h3&gt;

&lt;p&gt;Our app asks Okta for a list of users and each user’s groups, but Okta won’t hand that over merely because we asked nicely. You must grant your application permission to make those calls.&lt;/p&gt;

&lt;p&gt;Back in your Okta org, we have a couple of changes to make:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Applications and Resources&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select the application you created&lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;General&lt;/strong&gt; tab, find the &lt;strong&gt;General Settings&lt;/strong&gt; section and press &lt;strong&gt;Edit&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Activate the &lt;strong&gt;Proof of possession&lt;/strong&gt; checkbox then press &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Open the &lt;strong&gt;Okta API Scopes&lt;/strong&gt; tab, find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta.users.read&lt;/code&gt;, and press &lt;strong&gt;Grant&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That single scope covers both calls this app makes. Listing a user’s groups counts as reading a user resource, so you don’t need &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta.groups.read&lt;/code&gt; as well. Handy!&lt;/p&gt;

&lt;h3 id=&quot;allow-cross-origin-requests-and-protect-the-access-token&quot;&gt;Allow cross-origin requests and protect the access token&lt;/h3&gt;

&lt;p&gt;Before the app can talk to Okta from a browser, we need to allow cross-origin requests:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Security&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt; &amp;gt; &lt;strong&gt;Trusted Origins&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Confirm you see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:4200&lt;/code&gt; with both &lt;strong&gt;CORS&lt;/strong&gt; and &lt;strong&gt;Redirect&lt;/strong&gt; enabled&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’re taking a shortcut in the demo by calling the Okta management API directly from the Angular app. Okta’s APIs are a backend that’s already in place, which means we can take the SDK for a spin without having to stand up resource APIs ourselves.&lt;/p&gt;

&lt;p&gt;The access token to call Okta’s APIs can perform sensitive admin actions, though, so let’s not send it around as a plain bearer token. Demonstrating Proof of Possession (DPoP) binds the token to a key pair that this browser generates and keeps to itself, so a stolen copy does a thief no good. The starter’s interceptor already sends the DPoP headers on every Okta request, so all that’s left is switching it on.&lt;/p&gt;

&lt;p&gt;If you want to learn more about DPoP in an Angular app, you’ll want to check out:&lt;/p&gt;

&lt;article class=&quot;link-container&quot; style=&quot;border: 1px solid silver; border-radius: 3px; padding: 12px 15px&quot;&gt;
              &lt;a href=&quot;/blog/2024/09/10/angular-dpop-jwt&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Secure OAuth 2.0 Access Tokens with Proofs of Possession&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Elevate access token security by demonstrating proof of possession (DPoP). Migrate a SPA to use DPoP in this hands-on project.&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/alisa-duncan/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-alisa_duncan-b29fa4df50f5c99f536307c6bc0e5cb3434a922bdada7fe4f4b3cf8488299465.jpg&quot; alt=&quot;avatar-avatar-alisa_duncan.jpeg&quot; class=&quot;BlogPost-avatar&quot; /&gt;
            &lt;/a&gt;
            &lt;span class=&quot;BlogPost-author&quot;&gt;
                &lt;a href=&quot;/blog/authors/alisa-duncan/&quot;&gt;Alisa Duncan&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;h2 id=&quot;add-authentication-with-the-standalone-okta-angular-provider&quot;&gt;Add authentication with the standalone Okta Angular provider&lt;/h2&gt;

&lt;p&gt;Here’s the part I’ve been looking forward to. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/app.config.ts&lt;/code&gt;. You already have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaAuth&lt;/code&gt; instance. Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{yourOktaDomain}&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{yourClientId}&lt;/code&gt; with the issuer URL and the client ID from the previous section. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;issuer&lt;/code&gt; value should look something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://integrator-123.okta.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple of OIDC properties worth calling out:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scopes&lt;/code&gt;&lt;/strong&gt; includes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta.users.read&lt;/code&gt; alongside the usual OIDC scopes, which is how the access token ends up carrying the permission you granted a moment ago&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dpop: true&lt;/code&gt;&lt;/strong&gt; is the client half of the DPoP setting you switched on in the Okta app configuration. The SDK handles the key pair and the proof headers from here&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the OIDC configuration set, now it’s time to provide Okta authentication to the app. Update the app as shown:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApplicationConfig&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;na&quot;&gt;providers&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;// other providers such as provideRouter and provideHttpClient here&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;provideOktaAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;withOktaConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;oktaAuth&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;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s talk through the code.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideOktaAuth()&lt;/code&gt;&lt;/strong&gt; is the provider function that registers the SDK. It sits in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;providers&lt;/code&gt; array next to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideRouter&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideHttpClient&lt;/code&gt;, exactly like every other Angular provider function&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withOktaConfig({ oktaAuth })&lt;/code&gt;&lt;/strong&gt; passes your configuration in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve added Okta to an Angular app before, compare that to what you used to write:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;nx&quot;&gt;importProvidersFrom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;OktaAuthModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;forRoot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;oktaAuth&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;nx&quot;&gt;OktaAuth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* config */&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaAuthModule&lt;/code&gt; is gone, and so is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;importProvidersFrom&lt;/code&gt; wrapper that existed only to drag an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NgModule&lt;/code&gt; into a standalone app. Nothing left to bridge. That’s my favorite kind of API change – the kind where code disappears. 🎉&lt;/p&gt;

&lt;h2 id=&quot;protect-routes-with-functional-route-guards&quot;&gt;Protect routes with functional route guards&lt;/h2&gt;

&lt;p&gt;The dashboard route is wide open right now. Before we close it, let’s see the problem. Start 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;npm start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Navigate straight to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:4200/dashboard&lt;/code&gt; without signing in. The page renders, the API calls fail, and you get an error message. Not a great experience for anyone.&lt;/p&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/app.routes.ts&lt;/code&gt; and add the guard to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dashboard&lt;/code&gt; route:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;OktaCallbackComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;canActivateAuthGuard&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@okta/okta-angular&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Routes&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;c1&quot;&gt;// other routes here. We're only updating the dashboard route&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;loadComponent&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;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./dashboard/dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Dashboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;canActivate&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;nx&quot;&gt;canActivateAuthGuard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// add the guard here&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 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canActivateAuthGuard&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CanActivateFn&lt;/code&gt;, so it’s a function you drop into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canActivate&lt;/code&gt; array. There’s no guard class to instantiate and no provider to register. It has two siblings worth knowing about: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canActivateChildAuthGuard&lt;/code&gt; for child routes, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canMatchAuthGuard&lt;/code&gt;, which pairs nicely with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loadChildren&lt;/code&gt; because it can reject a route before Angular downloads the lazy-loaded bundle.&lt;/p&gt;

&lt;p&gt;Try navigating to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dashboard&lt;/code&gt; while signed out now. Okta redirects you to sign in first. Much better!&lt;/p&gt;

&lt;h2 id=&quot;add-users-to-your-okta-org&quot;&gt;Add users to your Okta org&lt;/h2&gt;

&lt;p&gt;When we sign in, we see a user table. And you’re in it!&lt;/p&gt;

&lt;p&gt;However, a user portal with only one user isn’t much of a portal, so let’s add a few people to look at. Back in Okta:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Directory&lt;/strong&gt; &amp;gt; &lt;strong&gt;People&lt;/strong&gt; and press &lt;strong&gt;Add person&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Fill in with your favorite first name, last name, and username (email), then press &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Repeat a couple of times&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add a totally made-up set of names for more fun. I went with Rosalind Park, Tobias Ferreira, and Ines Alvarez, but this is your org, and I’m not the boss of you.&lt;/p&gt;

&lt;p&gt;Ensure you assign the users to your Okta app. Depending on your Okta app’s access policy, users may be automatically assigned to apps, but it’s &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/users-groups-profiles/usgp-assign-apps.htm&quot;&gt;always good to double check&lt;/a&gt;. You went through the effort to create users, so you want to see them in your dashboard, right?&lt;/p&gt;

&lt;p&gt;Every user you create automatically joins the built-in &lt;strong&gt;Everyone&lt;/strong&gt; group, which means the groups view you’re about to build has something to show from the first run. Thanks, Okta!&lt;/p&gt;

&lt;h2 id=&quot;display-users-and-review-angulars-rxresource-api&quot;&gt;Display users and review Angular’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; API&lt;/h2&gt;

&lt;p&gt;Time to see what we have. Back in the running app, take a closer look at that table.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/angular-auth-signals/app-dashboard-61ad6637c8d7cde66505c35bc4c846eab86b3c3120fd2d721b52d25451444017.jpg&quot; alt=&quot;The user portal dashboard showing a table of users with name, email, and status columns, and a toggle button on each row&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Each row has a toggle button that currently does nothing. We’ll get there.&lt;/p&gt;

&lt;p&gt;Before we do, open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/dashboard/dashboard.ts&lt;/code&gt; and look at how that table gets its data:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;nx&quot;&gt;usersResource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rxResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;stream&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;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;okta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;defaultValue&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt;, the signal-based way to wrap an observable that loads data. You give it a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stream&lt;/code&gt;, and it hands back a resource exposing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isLoading()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error()&lt;/code&gt; as signals, which the template reads directly with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@if&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@for&lt;/code&gt;. No &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AsyncPipe&lt;/code&gt;, no subscription to unwind.&lt;/p&gt;

&lt;p&gt;This resource loads once and never changes, because nothing it depends on ever changes. The groups resource you’re about to write is interesting because it has to reload every time you pick a different user.&lt;/p&gt;

&lt;h2 id=&quot;load-a-users-groups-with-rxresource-and-a-signal-input&quot;&gt;Load a user’s groups with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; and a signal input&lt;/h2&gt;

&lt;p&gt;Generate a component for the groups view:&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;ng g c UserGroups &lt;span class=&quot;nt&quot;&gt;--style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;none &lt;span class=&quot;nt&quot;&gt;--skip-tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/user-groups/user-groups.ts&lt;/code&gt; and replace the contents with this:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rxResource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core/rxjs-interop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Okta&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;OktaService&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;../okta&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;OktaUser&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;../okta.types&lt;/span&gt;&lt;span class=&quot;dl&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;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-user-groups&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;imports&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;na&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./user-groups.html&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserGroups&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;oktaService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OktaService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OktaUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;groupsResource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rxResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;params&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;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;stream&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;na&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;oktaService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;userId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;defaultValue&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;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s step through the key parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user = input.required&amp;lt;OktaUser&amp;gt;()&lt;/code&gt;&lt;/strong&gt; takes the user from the parent component. Inputs declared with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;input()&lt;/code&gt; function are signals, which matters a lot for the next line.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt;&lt;/strong&gt; is where the reactivity lives. Reading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this.user().id&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; subscribes the resource to that signal, so when the parent passes a different user, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; recomputes, and the resource fetches that user’s groups on its own. You don’t wire up an effect, and you don’t call a reload method.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stream&lt;/code&gt;&lt;/strong&gt; receives the resolved &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; and returns the observable to subscribe to. Destructuring it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{ params: userId }&lt;/code&gt; keeps the call readable.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;defaultValue: []&lt;/code&gt;&lt;/strong&gt; means &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value()&lt;/code&gt; is an empty array instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt; before the first response, so the template doesn’t need a null check.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create two computed properties, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groupCount&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayGroups&lt;/code&gt; properties, in the component below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groupsResource&lt;/code&gt; definition:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;nx&quot;&gt;groupCount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;groupsResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;displayGroups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;groupsResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slice&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;mi&quot;&gt;3&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 two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;computed&lt;/code&gt; properties derive from the resource, and they intentionally disagree with each other. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groupCount&lt;/code&gt; reports every group the user belongs to, while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayGroups&lt;/code&gt; caps the list at three. Users in a real org can belong to a long list of groups, and I’d rather show an honest count than let a wall of panels shove the table off your screen.&lt;/p&gt;

&lt;p&gt;Now the template. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/user-groups/user-groups.html&lt;/code&gt; and add:&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;nt&quot;&gt;&amp;lt;h2&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mb-4 text-sm font-semibold text-gray-700&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  {{ user().profile.firstName }} {{ user().profile.lastName }} groups
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

@if (groupsResource.isLoading()) {
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-sm text-gray-500&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Loading groups...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
} @else if (groupsResource.error()) {
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-sm text-red-600&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;role=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;alert&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Failed to load groups.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
} @else {
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;py-3&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;Group count: {{ groupCount() }}&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&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;grid gap-3 sm:grid-cols-2&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    @for (group of displayGroups(); track group.id) {
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;show group info here&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    } @empty {
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;p&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-sm text-gray-500&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;No groups found.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    }
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice the template reads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isLoading()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error()&lt;/code&gt; straight off the resource, so the loading and failure states come along for free. We’ll replace that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show group info here&lt;/code&gt; placeholder in the next section.&lt;/p&gt;

&lt;p&gt;The next step is handing the component a user. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/dashboard/dashboard.html&lt;/code&gt; and find the row that appears when you toggle a user. Replace its placeholder content with the new component:&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;nt&quot;&gt;&amp;lt;app-user-groups&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;[user]=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;user&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserGroups&lt;/code&gt; to the dashboard’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;imports&lt;/code&gt; array in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/dashboard/dashboard.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;UserGroups&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;../user-groups/user-groups&lt;/span&gt;&lt;span class=&quot;dl&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;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-dashboard&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;imports&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;nx&quot;&gt;UserGroups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./dashboard.html&lt;/span&gt;&lt;span class=&quot;dl&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;Feel free to start the app and press the toggle on a user. You’ll see a group count of 1 and a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;show group info here&lt;/code&gt; for the &lt;strong&gt;Everyone&lt;/strong&gt; group. Toggle a different user and watch the panel refill itself – that’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;params&lt;/code&gt; doing its job. Pretty sweet!&lt;/p&gt;

&lt;h3 id=&quot;display-group-details-using-signal-inputs&quot;&gt;Display group details using signal inputs&lt;/h3&gt;

&lt;p&gt;The placeholder has served us well, and now it can go. Generate a component for a single group:&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;ng g c user-groups/group-panel &lt;span class=&quot;nt&quot;&gt;--inline-template&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--style&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;none &lt;span class=&quot;nt&quot;&gt;--skip-tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/user-groups/group-panel/group-panel.ts&lt;/code&gt; and replace the contents:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;@angular/core&lt;/span&gt;&lt;span class=&quot;dl&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;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-group-panel&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
    &amp;lt;div class=&quot;rounded-xl border border-gray-200 bg-white p-4 shadow-sm&quot;&amp;gt;
      &amp;lt;h3 class=&quot;mb-1 text-base font-semibold text-gray-800&quot;&amp;gt;{{ heading() }}&amp;lt;/h3&amp;gt;
      &amp;lt;p class=&quot;text-sm text-gray-600&quot;&amp;gt;{{ description() }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GroupPanel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;heading&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This component has no services, no resources, and no idea Okta exists. It takes two inputs and renders them; the separation of concerns is exactly what we want. It makes this component reusable and minimal. Note &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;description&lt;/code&gt; has a default of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;''&lt;/code&gt;, because group descriptions in Okta are optional.&lt;/p&gt;

&lt;p&gt;Back in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/app/user-groups/user-groups.html&lt;/code&gt;, swap the placeholder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;show group info here&amp;lt;/p&amp;gt;&lt;/code&gt; for the panel:&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;nt&quot;&gt;&amp;lt;app-group-panel&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;[heading]=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;group.profile.name&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;[description]=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;group.profile.description ?? ''&quot;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then import it into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UserGroups&lt;/code&gt; component:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;GroupPanel&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./group-panel/group-panel&lt;/span&gt;&lt;span class=&quot;dl&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;nd&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;app-user-groups&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;imports&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;nx&quot;&gt;GroupPanel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;templateUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;./user-groups.html&lt;/span&gt;&lt;span class=&quot;dl&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;Start the app and toggle a user open.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/angular-auth-signals/app-user-groups-be33697fcbb7e30a57f7787c37a9223a44df56760959399ca76d241ee991188a.jpg&quot; alt=&quot;The dashboard with a user row expanded, showing a group count and a panel for the Everyone group with its name and description&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This app is basic, but the data is real, and it reloads when it should. 🏆&lt;/p&gt;

&lt;p&gt;One group per user works, but it could be more exciting. We can create more groups and add our users to them.&lt;/p&gt;

&lt;p&gt;Back in your Okta org, navigate to &lt;strong&gt;Directory&lt;/strong&gt; &amp;gt; &lt;strong&gt;Groups&lt;/strong&gt;, press &lt;strong&gt;Add group&lt;/strong&gt;, and create a group with a name and description. Then add a couple of your users to it. Read &lt;a href=&quot;https://help.okta.com/en-us/content/topics/users-groups-profiles/usgp-groups-main.htm&quot;&gt;Manage groups&lt;/a&gt; if you want the full details. Toggle that user open again, and you’ll see the new panel show up alongside &lt;strong&gt;Everyone&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;load-your-okta-configuration-at-runtime&quot;&gt;Load your Okta configuration at runtime&lt;/h2&gt;

&lt;p&gt;Right now, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.config.ts&lt;/code&gt; hardcodes the Okta configuration, so changing your issuer or client ID means a rebuild and a redeploy. If you’d rather fetch those values when the app starts, the new SDK still supports it. I wrote about this pattern in &lt;a href=&quot;/blog/2024/02/28/okta-authentication-angular&quot;&gt;Flexible Authentication Configurations in Angular Applications Using Okta&lt;/a&gt;, and the good news is it survived the move to standalone providers.&lt;/p&gt;

&lt;p&gt;The change has two halves. Set the configuration during app initialization and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideOktaAuth()&lt;/code&gt; with no configuration. Something like this:&lt;/p&gt;

&lt;div class=&quot;language-ts 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;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;configInitializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;configService&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;inject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;OktaAuthConfigService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;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;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/api/config.json&lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ok&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;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;`Couldn't load the Okta configuration: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authConfig&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;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;configService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;setConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;oktaAuth&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;nx&quot;&gt;OktaAuth&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;nx&quot;&gt;authConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;redirectUri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/login/callback`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;scopes&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;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;openid&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;offline_access&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;okta.users.read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;pkce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dpop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&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;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ApplicationConfig&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;na&quot;&gt;providers&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;// remaining providers&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;provideAppInitializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;configInitializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;provideOktaAuth&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s talk through what changed.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideOktaAuth()&lt;/code&gt;&lt;/strong&gt; now takes no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;withOktaConfig()&lt;/code&gt; feature. The SDK treats its configuration as optional at provide time, and builds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OktaAuth&lt;/code&gt; instance the first time something injects it, which is after initialization finishes.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideAppInitializer()&lt;/code&gt;&lt;/strong&gt; replaces the old &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APP_INITIALIZER&lt;/code&gt; multi-provider. It takes a plain function, so you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inject()&lt;/code&gt; your dependencies inside it rather than declaring a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deps&lt;/code&gt; array. Return a promise, and Angular waits for it before starting the app.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt;&lt;/strong&gt; reads the configuration. We’re not using Angular’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt; here, and we’re not coding workarounds to get around the interceptor either.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;response.ok&lt;/code&gt;&lt;/strong&gt; guards the parse. When that path 404s, most servers hand back an HTML error page, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;response.json()&lt;/code&gt; fails with a parse error that tells you nothing about what actually went wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One sequencing detail that will bite you later if you don’t know it now: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inject()&lt;/code&gt; has to run &lt;em&gt;before&lt;/em&gt; your first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;await&lt;/code&gt;. Once you await, you’ve left the injection context and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inject()&lt;/code&gt; throws. Evaluating it in the parameter list works, as does calling it on the first line of the body. 👀&lt;/p&gt;

&lt;p&gt;Runtime configuration lets you point the same app to a different Okta org without rebuilding it. Still the ultimate flexibility!&lt;/p&gt;

&lt;p&gt;Before you move on, let’s talk about what belongs in that config file. Fetching it without an access token is fine here. The request stays on your own origin, and the values it returns are already public. The JavaScript bundle includes your issuer and client ID. Anyone can read them out of your app today.&lt;/p&gt;

&lt;table&gt;
&lt;tr&gt;
    &lt;td style=&quot;font-size: 3rem;&quot;&gt;⚠️&lt;/td&gt;
    &lt;td&gt;
      &lt;strong&gt;Heads up!&lt;/strong&gt; &lt;br /&gt;
      Anyone can request &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/config.json&lt;/code&gt;, so nothing sensitive goes in it. This is a browser-based app, which means it cannot keep a secret. A single-page app can’t maintain secrets. If a value would hurt you when published, it belongs on a server you control.
    &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id=&quot;learn-more-about-angular-signals-standalone-apis-and-oidc&quot;&gt;Learn more about Angular signals, standalone APIs, and OIDC&lt;/h2&gt;

&lt;p&gt;In this post, you added authentication to a standalone Angular app with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;provideOktaAuth&lt;/code&gt;, protected a route with a functional guard, and loaded each user’s groups with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rxResource&lt;/code&gt; driven by a signal input. I hope you enjoyed it! You can find the completed project in the &lt;a href=&quot;https://github.com/oktadev/okta-angular-auth-signals-example&quot;&gt;okta-angular-auth-signals-example&lt;/a&gt; GitHub repository.&lt;/p&gt;

&lt;p&gt;If you liked this post, check out these resources.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2024/02/28/okta-authentication-angular&quot;&gt;Flexible Authentication Configurations in Angular Applications Using Okta&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2024/09/10/angular-dpop-jwt&quot;&gt;Secure OAuth 2.0 Access Tokens with Proofs of Possession&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2024/03/12/stepup-authentication&quot;&gt;Add Step-up Authentication Using Angular and NestJS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://angular.dev/guide/signals/resource&quot;&gt;Angular’s resource and rxResource guide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/okta/okta-angular&quot;&gt;Okta Angular SDK on GitHub&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; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/oktadev&quot;&gt;YouTube&lt;/a&gt; for more exciting content. We also want to hear from you about topics you’d like to see and any questions you may have. Leave us a comment below!&lt;/p&gt;
</description>
        <pubDate>Tue, 25 Aug 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/08/25/angular-auth-signals</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/08/25/angular-auth-signals</guid>
      </item>
    
      <item>
        <title>Add Cross App Access to Your OIDC Resource Application</title>
        <description>&lt;p&gt;If you currently federate enterprise customers using OpenID Connect (OIDC) and want to allow applications to access your API on behalf of those users, 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 designed with OIDC in mind. Your authorization server already trusts the customer’s IdP for single sign-on (SSO), and XAA reuses that same trust for API access. This guide details what you need to support, how to validate the grant, and how to resolve the user 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-oidc-works&quot; id=&quot;markdown-toc-how-xaa-in-oidc-works&quot;&gt;How XAA in OIDC 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-oidc-federated-applications&quot; id=&quot;markdown-toc-xaa-implementation-checklist-for-oidc-federated-applications&quot;&gt;XAA implementation checklist for OIDC-federated applications&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#mapping-user-identity-from-iss-and-sub&quot; id=&quot;markdown-toc-mapping-user-identity-from-iss-and-sub&quot;&gt;Mapping user identity from &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;/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-oidc-app-securely&quot; id=&quot;markdown-toc-making-cross-application-requests-from-your-oidc-app-securely&quot;&gt;Making cross-application requests from your OIDC app securely&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configure-your-xaa-oidc-resource-app-in-okta&quot; id=&quot;markdown-toc-configure-your-xaa-oidc-resource-app-in-okta&quot;&gt;Configure your XAA OIDC Resource app in Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-oidc-requesting-app-for-testing&quot; id=&quot;markdown-toc-create-the-oidc-requesting-app-for-testing&quot;&gt;Create the OIDC requesting app for testing&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#create-the-oidc-resource-app-in-okta&quot; id=&quot;markdown-toc-create-the-oidc-resource-app-in-okta&quot;&gt;Create the OIDC resource app in Okta&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-oidc-sso&quot; id=&quot;markdown-toc-configure-oidc-sso&quot;&gt;Configure OIDC SSO&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-also-have-saml-apps&quot; id=&quot;markdown-toc-takeaways-for-implementors-who-also-have-saml-apps&quot;&gt;Takeaways for implementors who also have SAML apps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-cross-app-access-oidc-and-oauth-20&quot; id=&quot;markdown-toc-learn-more-about-cross-app-access-oidc-and-oauth-20&quot;&gt;Learn more about Cross App Access, OIDC, and OAuth 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-xaa-in-oidc-works&quot;&gt;How XAA in OIDC 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 OIDC integration untouched.&lt;/p&gt;

&lt;p&gt;The sequence diagram shown below describes the OIDC XAA flow. Notice that the OIDC 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 subject, and return an access token that you’ll use for resource requests.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-oidc-resource/xaa-oidc-sequence-diagram-e66c7e69cde77866c0cabc9ffb0dec6b03f34a0fd1d5f0b2ff49551799f04ea5.svg&quot; alt=&quot;Sequence diagram showing OIDC SSO between the user and Okta IdP, a token exchange producing an ID-JAG, and the resource authorization server redeeming the ID-JAG and resolving the subject 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 participating in the client’s SSO here. The only artifact crossing from the IdP to your domain is the ID-JAG. Everything upstream, including authentication, ID token issuance, and the token exchange, happens without you. 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;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;iss&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;&lt;/strong&gt;: Together, these are the primary key for user resolution. Neither one is unique on its own&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 issuer identifier for your 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;: The unique ID for this ID-JAG JWT, as required by JWT conventions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three optional claims matter if you run a multi-tenant service: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tenant&lt;/code&gt; claim scopes the subject when the IdP itself is multi-tenant, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud_tenant&lt;/code&gt; scopes the subject when &lt;em&gt;your&lt;/em&gt; resource authorization server is multi-tenant, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud_sub&lt;/code&gt; carries the identifier that the IdP believes &lt;em&gt;you&lt;/em&gt; already have for this user.&lt;/p&gt;

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

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

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#mapping-user-identity-from-iss-and-sub&quot;&gt;Mapping user identity from &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;/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;h3 id=&quot;mapping-user-identity-from-iss-and-sub&quot;&gt;Mapping user identity from &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;/h3&gt;

&lt;p&gt;You already resolve users from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim during SSO, so the ID-JAG uses the identifier you know. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim alone is not the key, though.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; value is only unique when scoped with the issuer. Resolve on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; together. If the IdP is multi-tenant, the scope is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tenant&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;, and you’ll need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tenant&lt;/code&gt; claim to disambiguate. Two customers on the same multi-tenant IdP can otherwise collide.&lt;/p&gt;

&lt;p&gt;If the user hasn’t signed in to your app yet, there’s no local record to resolve. Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt; claim, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud_sub&lt;/code&gt; when the IdP supplies it, to provision just in time. Treat &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;email&lt;/code&gt; as a linking hint rather than a primary key. Email addresses get reassigned when employees leave, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iss&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; does not.&lt;/p&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 IdP connection to prevent forgery.&lt;/p&gt;

&lt;p&gt;If you verify the signature before checking the issuer binding, an attacker could stand up their own IdP, sign a token, and present a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; belonging to one of your users.&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;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;multiTenant&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;allowJit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;true&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;resolveSubject&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;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;resolveSubject&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;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;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub&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;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;multiTenant&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;tenant&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_federated_id&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;iss&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;tenant&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&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;ow&quot;&gt;and&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;allowJit&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;provision_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;iss&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;tenant&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&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;email&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 return to the IdP for a new one.&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;p&gt;Publish the profile identifier and nothing more. Don’t list the issuers you trust in public metadata, since that discloses your customer relationships. If clients need to check whether you accept a given issuer, put that check behind client authentication.&lt;/p&gt;

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

&lt;p&gt;With these four steps complete, you’ve configured your OIDC application for Cross App Access. Agents can now authorize requests against your API using the federation you already run.&lt;/p&gt;

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

&lt;p&gt;Before you configure anything, here’s who runs what in this walkthrough:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;Role&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Your resource server and API&lt;/td&gt;
      &lt;td&gt;Runs on your infrastructure. The service XAA protects.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Your resource authorization server (your resource AS)&lt;/td&gt;
      &lt;td&gt;Runs on your infrastructure. Mints and validates the access tokens your API accepts.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Your Okta org&lt;/td&gt;
      &lt;td&gt;The IdP. Authenticates users and issues the ID-JAG.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;The Okta app integration representing your resource app&lt;/td&gt;
      &lt;td&gt;A registration in Okta, not a running service. Holds the XAA configuration Okta uses to mint ID-JAGs for your resource AS.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;The Okta app integration representing the requesting app&lt;/td&gt;
      &lt;td&gt;A registration in Okta, not a running service. Lets users sign in to the AI agent and lets Okta issue ID-JAGs on its behalf.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;xaa.dev&lt;/td&gt;
      &lt;td&gt;Plays the requesting app for this walkthrough, running against your resource server.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;xaa.dev drives the requesting side so you can verify the resource side, which is the only side you’re actually building.&lt;/p&gt;

&lt;h2 id=&quot;configure-your-xaa-oidc-resource-app-in-okta&quot;&gt;Configure your XAA OIDC Resource app in Okta&lt;/h2&gt;

&lt;p&gt;This walkthrough tests an XAA implementation, it doesn’t build one. Before you continue, you’ll need already running:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Your resource AS, with a reachable issuer URL and token endpoint&lt;/li&gt;
  &lt;li&gt;The ability to register an OAuth client at your resource AS and issue it a client ID and secret, since the requesting app authenticates there at redemption&lt;/li&gt;
  &lt;li&gt;The &lt;a href=&quot;#xaa-implementation-checklist-for-oidc-federated-applications&quot;&gt;ID-JAG redemption logic from the checklist&lt;/a&gt; earlier in this post, implemented&lt;/li&gt;
  &lt;li&gt;An API endpoint that accepts the access token your resource AS issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t built these yet, work through the checklist sections above first.&lt;/p&gt;

&lt;p&gt;You’ll also need:&lt;/p&gt;

&lt;ul&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/developer/test-resource-app?tab=oidc&quot;&gt;xaa.dev&lt;/a&gt; to drive the requesting side while you test your resource server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the guide’s &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#overview&quot;&gt;prerequisites for AI agent-to-app XAA&lt;/a&gt; for the underlying Okta org requirements.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the &lt;a href=&quot;https://xaa.dev/developer/test-resource-app?tab=oidc&quot;&gt;xaa.dev resource app tester&lt;/a&gt; and enter any validly formatted email address to establish a session&lt;/li&gt;
  &lt;li&gt;In &lt;strong&gt;Your IdP’s issuer URL&lt;/strong&gt;, enter your Okta org issuer URL. This is the IdP field, not the resource AS issuer URL field further down the panel. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://integrator-xxxxxx.okta.com&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Copy the generated &lt;strong&gt;Sign-in redirect URI&lt;/strong&gt;, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://auth.resource.xaa.dev/api/federated-sso/callback/resapp-oidc-xxxxxxx&lt;/code&gt;. You’ll paste it into the requesting app integration in the next section&lt;/li&gt;
  &lt;li&gt;Leave the tab open. You’ll return to it after the Okta configuration is complete&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;create-the-oidc-requesting-app-for-testing&quot;&gt;Create the OIDC requesting app for testing&lt;/h3&gt;

&lt;p&gt;You’ll need a requesting app integration and a resource app integration set up in Okta. Create the requesting app integration by following the guide’s &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#supported-resource-apps&quot;&gt;OIDC app creation steps&lt;/a&gt;, using these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App integration name&lt;/strong&gt;: “Requesting App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Grant type&lt;/strong&gt;: Authorization Code, plus &lt;strong&gt;Refresh Token&lt;/strong&gt; — you’ll need it to request the ID-JAG&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt;: the &lt;strong&gt;Sign-in redirect URI&lt;/strong&gt; you copied from xaa.dev above&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Assignments&lt;/strong&gt;: keep &lt;strong&gt;Skip group assignments for now&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Select the &lt;strong&gt;General&lt;/strong&gt; tab and copy the &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client secret&lt;/strong&gt;. Paste both into the requesting app fields at &lt;a href=&quot;https://xaa.dev/developer/test-resource-app?tab=oidc&quot;&gt;xaa.dev&lt;/a&gt; and save.&lt;/p&gt;

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

&lt;p&gt;Assign your test user to the requesting app integration. See &lt;a href=&quot;https://help.okta.com/okta_help.htm?type=oie&amp;amp;id=ext-lcm-assign-app-user&quot;&gt;Assign an app integration to a user&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Note: The values here come from your Resource Authorization Server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;create-the-oidc-resource-app-in-okta&quot;&gt;Create the OIDC resource app in Okta&lt;/h3&gt;

&lt;p&gt;Create the resource app integration the same way you created the requesting app integration, and use these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App integration name&lt;/strong&gt;: “Resource App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Grant type&lt;/strong&gt;: Authorization Code&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt;: any valid placeholder URL. This app integration never performs an interactive login in this walkthrough; it only holds the Resource Server XAA configuration and the user assignment, and Okta requires the field to be non-empty. Don’t reuse the xaa.dev URI here&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Assignments&lt;/strong&gt;: keep &lt;strong&gt;Skip group assignments for now&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After creating the app integration, select the &lt;strong&gt;General&lt;/strong&gt; tab to get the &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client secret&lt;/strong&gt;. Use these credentials in your resource server.&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#enable-xaa-on-a-custom-app-integration&quot;&gt;Enable Cross App Access (XAA) on the resource app integration&lt;/a&gt; from the &lt;strong&gt;Resource Server&lt;/strong&gt; tab, and configure:
Note: The values here come from your test Requestor SSO App on xaa.dev.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Issuer URL&lt;/strong&gt;: Your resource AS issuer URL, the issuer of the authorization server you run. Entering it here tells Okta which external server to mint ID-JAGs for; Okta doesn’t host this server. 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;li&gt;&lt;strong&gt;Audience/tenant ID&lt;/strong&gt;: This is optional and not needed for this walkthrough&lt;/li&gt;
&lt;/ol&gt;

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

  &lt;p&gt;This &lt;strong&gt;Issuer URL&lt;/strong&gt; (your resource AS issuer URL) must exactly match the &lt;strong&gt;Resource AS issuer (ID-JAG audience)&lt;/strong&gt; field you’ll enter on xaa.dev during verification. A mismatch still produces a correctly signed ID-JAG, but redemption fails the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; check, and that’s the hardest failure in this tutorial to diagnose.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Assign your test user to the resource app integration. See &lt;a href=&quot;https://help.okta.com/okta_help.htm?type=oie&amp;amp;id=ext-lcm-assign-app-user&quot;&gt;Assign an app integration to a user&lt;/a&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 requesting app integration and resource app integration configured, register a new AI Agent in Okta. During registration, you’ll link your requesting app integration under &lt;strong&gt;User access and authentication&lt;/strong&gt;, register the agent’s own OAuth client, and then connect the resource app integration as a &lt;strong&gt;Resource Connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Register the AI Agent and link your requesting app integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#register-ai-agent-user-access-and-authentication&quot;&gt;Register the AI Agent and link its requesting app integration&lt;/a&gt;. Use these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: “Agent”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Allow users to access this agent&lt;/strong&gt;: &lt;strong&gt;Select an existing app&lt;/strong&gt;, then choose “Requesting App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Add owners&lt;/strong&gt;: assign your test user as an individual owner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This linked app integration is what your users sign in through to reach the agent: they authenticate against it, and the agent then acts on their behalf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect the resource app integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#configure-the-xaa-connection&quot;&gt;Add a resource connection to the AI agent&lt;/a&gt;. Use these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Application instance&lt;/strong&gt;: “Resource App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AI agent’s client ID registered in this app&lt;/strong&gt;: “this app” here means your resource AS, not the Okta app integration. Enter the client ID you issued to the AI agent when you registered it as an OAuth client at your resource AS. You perform that registration outside Okta, and the process varies by product&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scopes&lt;/strong&gt;: &lt;strong&gt;Allow any scope&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Activate the AI Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Activating the linked requesting app integration usually activates the AI Agent. If the agent’s status is &lt;strong&gt;STAGED&lt;/strong&gt;, &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#activate-the-ai-agent&quot;&gt;activate it manually&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once the AI Agent is active, the configuration is complete. Except for Machine access, 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;Open the AI Agent you created, then go to &lt;strong&gt;Client registration&lt;/strong&gt; tab: You’ll see the agent uses the same credentials as the linked requesting app. We will use this client ID and secret to request an ID-JAG from the IdP.&lt;/p&gt;

&lt;p&gt;In xaa.dev, enter the following values:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Client ID&lt;/strong&gt;: Use the values from the AI Agent’s client registration tab.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Client Secret&lt;/strong&gt;: Use the values from the AI Agent’s client registration tab.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resource AS issuer (ID-JAG audience)&lt;/strong&gt;: Use your resource AS issuer URL. This must exactly match the &lt;strong&gt;Issuer URL&lt;/strong&gt; you set on the resource app integration’s Resource Server tab.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scopes&lt;/strong&gt;: Enter the scopes you want to request for the access token. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chat:read chat:write&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Press &lt;strong&gt;Save&lt;/strong&gt; to store the values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By this point, you’ll have every value from the checklist and your one-time Okta setup in place (AI Agent, 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;h3 id=&quot;configure-oidc-sso&quot;&gt;Configure OIDC SSO&lt;/h3&gt;

&lt;p&gt;Press &lt;strong&gt;Start OIDC 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 authorization and token endpoints from your org’s discovery document and returned an ID token and a 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;. The tester posts the refresh token from sign-in to your IdP’s token endpoint and 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&lt;/code&gt; should be the identifier for the user who logged in. 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;The client ID and secret you issued to the AI agent when you registered it as an OAuth client at your resource AS. This must be the same client ID you entered in the resource connection step, or redemption fails the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; check&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;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 (xaa.dev adds the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization: Bearer&lt;/code&gt; header 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 either download the log or have a URL to share 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/xaa-oidc-resource/export-conformance-log-2f2681448de69cdc10b6e563ed39125e566c8132e423890cb2774a820349c1f0.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-also-have-saml-apps&quot;&gt;Takeaways for implementors who also have SAML apps&lt;/h2&gt;

&lt;p&gt;If some of your enterprise customers federate over SAML instead, here’s what changes when you extend XAA to those connections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The subject arrives in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub_id&lt;/code&gt; claim using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;saml-nameid&lt;/code&gt; format, rather than in &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 on &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-oidc-and-oauth-20&quot;&gt;Learn more about Cross App Access, OIDC, and OAuth 2.0&lt;/h2&gt;

&lt;p&gt;If this guide helped you implement Cross App Access with OIDC, 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>Mon, 24 Aug 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/08/24/xaa-oidc-resource</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/08/24/xaa-oidc-resource</guid>
      </item>
    
      <item>
        <title>Add Cross App Access to Your OIDC Requesting Application</title>
        <description>&lt;p&gt;If you currently federate enterprise customers using OpenID Connect (OIDC) and want to connect with third-party applications, 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 designed with OIDC in mind. Your app already holds an ID token after sign-in, but it’s the refresh token from that same sign-in that you exchange to reach a third-party app. 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-oidc-works&quot; id=&quot;markdown-toc-how-xaa-in-oidc-works&quot;&gt;How XAA in OIDC works&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#xaa-implementation-checklist-for-oidc-federated-applications&quot; id=&quot;markdown-toc-xaa-implementation-checklist-for-oidc-federated-applications&quot;&gt;XAA implementation checklist for OIDC-federated applications&lt;/a&gt;    &lt;ul&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;#call-the-resource-api&quot; id=&quot;markdown-toc-call-the-resource-api&quot;&gt;Call the resource API&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-oidc-app-securely&quot; id=&quot;markdown-toc-making-cross-application-requests-from-your-oidc-app-securely&quot;&gt;Making cross-application requests from your OIDC app securely&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#configure-your-xaa-oidc-requesting-app-in-okta&quot; id=&quot;markdown-toc-configure-your-xaa-oidc-requesting-app-in-okta&quot;&gt;Configure your XAA OIDC requesting app in Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#register-the-requesting-app-in-okta&quot; id=&quot;markdown-toc-register-the-requesting-app-in-okta&quot;&gt;Register the requesting app in Okta&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#register-the-test-resource-app-in-okta&quot; id=&quot;markdown-toc-register-the-test-resource-app-in-okta&quot;&gt;Register the test resource app in Okta&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#register-your-requesting-app-at-xaadev&quot; id=&quot;markdown-toc-register-your-requesting-app-at-xaadev&quot;&gt;Register your requesting app at xaa.dev&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;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-oidc-and-oauth-20&quot; id=&quot;markdown-toc-learn-more-about-cross-app-access-oidc-and-oauth-20&quot;&gt;Learn more about Cross App Access, OIDC, and OAuth 2.0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-xaa-in-oidc-works&quot;&gt;How XAA in OIDC 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 below describes the OIDC XAA flow and how your application fits into it. 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 the 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-oidc-requesting/xaa-oidc-sequence-diagram-9434e0a44bb3267295acbb2937d216602154205bbe0119b48bf2a9da4aa512d5.svg&quot; alt=&quot;Sequence diagram showing OIDC sign-in between the user and Okta IdP, a token exchange producing 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-oidc-federated-applications&quot;&gt;XAA implementation checklist for OIDC-federated applications&lt;/h2&gt;

&lt;p&gt;Follow the guide in this section to support XAA in your OIDC 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 sign-in step remains the same; however, you’ll add two 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 the ID-JAG token from Okta using the refresh token&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;li&gt;Use the access token to make the API request to the resource app&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Your OIDC application runs the authorization code flow as usual, requesting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offline_access&lt;/code&gt; scope, and receives an ID token and a refresh token at the callback. The ID token is still there for your app’s session, but the refresh token is the credential for the exchange, so hold on to it for the session.&lt;/p&gt;

&lt;p&gt;When your application needs resources from a third-party app, exchange the refresh token for an ID-JAG at Okta’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/token&lt;/code&gt; 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. Authenticate the request with the client credentials from your OIDC app using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret_post&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;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 sign-in&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_secret=&amp;lt;your_client_secret&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.&lt;/p&gt;

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

&lt;p&gt;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;nf&quot;&gt;GET&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/api/todos/&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;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Bearer &amp;lt;access_token&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&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 new ID-JAG using the same refresh token. Refresh tokens are long-lived, so you don’t need to send the user back to sign in again for every ID-JAG. Refresh tokens eventually expire or get revoked, too. If the IdP rejects it (you’ll see an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;invalid_grant&lt;/code&gt; error), send the user through sign-in again to get a fresh one.&lt;/p&gt;

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

&lt;p&gt;With these steps complete, you’ve configured your OIDC application for Cross App Access. Your app can now obtain access tokens for third-party resource apps, using the federation you already run.&lt;/p&gt;

&lt;p&gt;You can now use Okta to make cross-application requests from your OIDC app to third-party resource apps.&lt;/p&gt;

&lt;p&gt;Before you configure anything, here’s who runs what in this walkthrough:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;Role&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Your requesting app and its code&lt;/td&gt;
      &lt;td&gt;Runs on your infrastructure. The application XAA let’s reach third-party resource apps&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Your Okta org&lt;/td&gt;
      &lt;td&gt;The IdP. Authenticates your users and issues the ID-JAG&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;The Okta app integration representing your requesting app&lt;/td&gt;
      &lt;td&gt;A registration in Okta, not a running service. Let’s your users sign in and let’s Okta issue ID-JAGs on your app’s behalf&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;The Okta app integration representing the test resource app&lt;/td&gt;
      &lt;td&gt;A registration in Okta, not a running service. Holds the XAA configuration Okta uses to mint ID-JAGs for xaa.dev’s resource authorization server&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;xaa.dev’s resource authorization server&lt;/td&gt;
      &lt;td&gt;Runs on xaa.dev’s infrastructure, not yours. Mints and validates the access tokens the test resource app accepts&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;xaa.dev’s resource API server&lt;/td&gt;
      &lt;td&gt;Plays the test resource app for this walkthrough, running against your requesting app&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;xaa.dev stands in for the third-party resource app so you can verify the requesting side, which is the only side you’re actually building. Ownership here is the reverse of the companion resource-app post: there, xaa.dev played the requesting side while you built the resource side; here, xaa.dev plays the resource side while you build the requesting side.&lt;/p&gt;

&lt;h2 id=&quot;configure-your-xaa-oidc-requesting-app-in-okta&quot;&gt;Configure your XAA OIDC requesting app in Okta&lt;/h2&gt;

&lt;p&gt;Let’s configure your OIDC 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;h3 id=&quot;register-the-requesting-app-in-okta&quot;&gt;Register the requesting app in Okta&lt;/h3&gt;

&lt;p&gt;Create the Okta app integration representing your requesting app by following the guide’s &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#supported-requesting-apps&quot;&gt;OIDC app creation steps&lt;/a&gt;, using these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App integration name&lt;/strong&gt;: “Requesting App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Grant type&lt;/strong&gt;: Authorization Code, plus &lt;strong&gt;Refresh Token&lt;/strong&gt; under &lt;strong&gt;Core Grants&lt;/strong&gt;. You’ll need a Refresh Token to request the ID-JAG&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt;: the callback URL of your requesting app, e.g., “https://requester-app-uri/callback”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign-out redirect URIs&lt;/strong&gt;: the sign-out URL of your requesting app&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Assignments&lt;/strong&gt;: keep &lt;strong&gt;Skip group assignments for now&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After creating the app integration, copy the &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client secret&lt;/strong&gt; from the &lt;strong&gt;General&lt;/strong&gt; tab and add them to your requesting app. Your app uses these same credentials for sign-in and for the ID-JAG token exchange.&lt;/p&gt;

&lt;p&gt;Assign your test user to the requesting app integration. See &lt;a href=&quot;https://help.okta.com/okta_help.htm?type=oie&amp;amp;id=ext-lcm-assign-app-user&quot;&gt;Assign an app integration to a user&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With your requesting app integration registered, you’ll need an Okta app integration representing the test resource app next.&lt;/p&gt;

&lt;h3 id=&quot;register-the-test-resource-app-in-okta&quot;&gt;Register the test resource app in Okta&lt;/h3&gt;

&lt;p&gt;Create the Okta app integration representing the test resource app by following the guide’s &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#supported-resource-apps&quot;&gt;OIDC app creation steps&lt;/a&gt;, using these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App integration name&lt;/strong&gt;: “Resource App for Testing”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://idp.xaa.dev/oidc-resource/callback&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Assignments&lt;/strong&gt;: keep &lt;strong&gt;Skip group assignments for now&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; These values are for setup only; they don’t constitute a working single sign-on (SSO) connection to the test resource app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Assign your test user to this app integration the same way you did for the requesting app integration above.&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#enable-xaa-on-a-custom-app-integration&quot;&gt;Enable Cross App Access (XAA) on this app integration&lt;/a&gt; from the &lt;strong&gt;Resource Server&lt;/strong&gt; tab, and configure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Issuer URL&lt;/strong&gt;: Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://auth.resource.xaa.dev&lt;/code&gt;. This is xaa.dev’s resource authorization server, not yours. Entering it here tells Okta which external server to mint ID-JAGs for&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audience/tenant ID&lt;/strong&gt;: This is optional and not needed for this walkthrough&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;Note:&lt;/strong&gt; Use Audience/tenant ID when you have multiple tenants in your organization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-oidc-requesting/resource-app-config-c02b3f07771e0c9cc784d651972cea249e5693938b7d476f962aecfe9f71d05a.jpg&quot; alt=&quot;Resource Server tab showing Cross-app access (XAA) enabled with Issuer URL configured.&quot; width=&quot;1200&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With the test resource app integration configured in Okta, register your requesting app at xaa.dev next.&lt;/p&gt;

&lt;h3 id=&quot;register-your-requesting-app-at-xaadev&quot;&gt;Register your requesting app at xaa.dev&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href=&quot;https://xaa.dev/developer/test-requesting-app?tab=oidc&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;, for example, name1234…@okta.com. After you enter all values, click &lt;strong&gt;Register&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Registering here registers your requesting app as an OAuth client at xaa.dev’s resource authorization server, and returns a &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt;. Save them. Use this &lt;strong&gt;Client ID&lt;/strong&gt; in the AI agent’s resource connection in the next section.&lt;/p&gt;

&lt;p&gt;When you get to the token exchange later in this walkthrough, substitute these values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;audience&lt;/code&gt;&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://auth.resource.xaa.dev&lt;/code&gt;, xaa.dev’s resource authorization server (AS) issuer URL&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scope&lt;/code&gt;&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;todos.read&lt;/code&gt;, the scope required to call the test resource app’s API&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt;&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://api.resource.xaa.dev&lt;/code&gt;, xaa.dev’s resource API base URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this site open; you’ll return to it to run the validation flow after the Okta configuration is complete.&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 requesting app integration and the test resource app integration configured, register a new AI Agent in Okta. During registration, you’ll link your requesting app integration under &lt;strong&gt;User access and authentication&lt;/strong&gt;, register the agent’s own OAuth client, and then connect the test resource app integration as a &lt;strong&gt;Resource Connection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Register the AI Agent and link your requesting app integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#register-ai-agent-user-access-and-authentication&quot;&gt;Register the AI Agent and link its requesting app integration&lt;/a&gt;. Use these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: “Agent”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Allow users to access this agent&lt;/strong&gt;: &lt;strong&gt;Select an existing app&lt;/strong&gt;, then choose your requesting app integration (e.g., “Requesting App”)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Add owners&lt;/strong&gt;: assign your test user as an individual owner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This linked app integration is what your users sign in through to reach the agent: they authenticate against it, and the agent then acts on their behalf.&lt;/p&gt;

&lt;p&gt;Open the AI Agent you created, then go to the &lt;strong&gt;Client registration&lt;/strong&gt; tab: you’ll see the agent uses the same credentials as the linked requesting app integration. You’ll use this client ID and secret to request an ID-JAG from the IdP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect the test resource app integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#configure-the-xaa-connection&quot;&gt;Add a resource connection to the AI agent&lt;/a&gt;. Use these tutorial-specific values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Application instance&lt;/strong&gt;: “Resource App for Testing”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AI agent’s client ID registered in this app&lt;/strong&gt;: the &lt;strong&gt;Client ID&lt;/strong&gt; from xaa.dev (it should look similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byora...&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Scopes&lt;/strong&gt;: &lt;strong&gt;Allow any scope&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Activate the AI Agent&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Activating the linked requesting app integration usually activates the AI Agent. If the agent’s status is &lt;strong&gt;STAGED&lt;/strong&gt;, &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#activate-the-ai-agent&quot;&gt;activate it manually&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once the AI Agent is active, the configuration is complete. Except for Machine access, all checkmarks on the agent configuration page must be green.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-oidc-requesting/ai-agent-completion-d4c4c371231eeba749b906291759fd592df14ba77757a2483cac1796b7e00b42.jpg&quot; alt=&quot;AI Agent configuration page showing all required sections completed with green checkmarks except Machine access.&quot; width=&quot;1200&quot; class=&quot;center-image&quot; /&gt;&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 Okta setup is complete, sign in to your requesting app and trigger whatever action in your code path calls the test resource app, for example, a button that fetches its data. That single action should exercise all three checklist requests in sequence: your app requests the ID-JAG, redeems it for an access token at xaa.dev’s resource authorization server, and calls the test resource app’s API with that token.&lt;/p&gt;

&lt;p&gt;Then return to &lt;a href=&quot;https://xaa.dev/developer/test-requesting-app?tab=oidc&quot;&gt;xaa.dev&lt;/a&gt; and open 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;An access token was issued&lt;/li&gt;
  &lt;li&gt;Resource Server accepted your access token&lt;/li&gt;
  &lt;li&gt;The API call to /api/todos/ was successful&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the companion resource-app post, xaa.dev acts as the client, and you observe the result. Here, your requesting app acts as the client, and xaa.dev observes it.&lt;/p&gt;

&lt;p&gt;At this stage, the JSON conformance log has the complete details of the XAA flow. You can either download the log or share a URL with your IdP.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-oidc-requesting/requesting-conformance-log-success-c085d2269b9cb138bab3cf0764e45d507b992cbca5fb9aeae57b7cc5a4f2c284.jpg&quot; alt=&quot;xaa.dev Live verification tab showing a green Conformance passed panel after a successful XAA flow.&quot; width=&quot;1200&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

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

&lt;p&gt;If this guide helped you implement Cross App Access with OIDC, 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, 21 Aug 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/08/21/xaa-oidc-requesting</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/08/21/xaa-oidc-requesting</guid>
      </item>
    
      <item>
        <title>I Found My Coordinates: Code, Community, and Okta</title>
        <description>&lt;p&gt;It all started with a phone call from an institute that reached out to recruit me for its campus ambassador program. I was navigating my first year of college, figuring things out. It was all new for me, a new city, far from home, and a hostel room. I was not aware of anything except books and lectures.&lt;/p&gt;

&lt;p&gt;A senior at my campus ambassador program advised me to create a LinkedIn account and start looking for opportunities from day one. As they say, “You don’t have to be great to start, but you have to start to be great.” So I started applying for random internships. Soon, I received my first offer, where I discovered the power of networking and community.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“No one can whistle a symphony. It takes a whole orchestra to play it.”&lt;/em&gt; – H.E. Luccock&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Community has always been the best part of my life. The love and support we receive from each other are invaluable. I have always given to the community and gained far more in return.&lt;/p&gt;

&lt;p&gt;I attended an institution that wasn’t particularly well-known. There weren’t many communities, so why not do something about it? Through professional platforms, I discovered Google Developer Student Clubs (GDSC), researched the opportunity, and applied. I became the pioneer GDSC Lead at my institution, and one of 280 leads across India.&lt;/p&gt;

&lt;p&gt;We built the community from scratch, organizing meetups, workshops, and hackathons that delivered real value to students. These experiences sparked curiosity and helped students find their voice. When I stumbled across the Developer Advocate role, I was very happy and curious, so I started digging into it.&lt;/p&gt;

&lt;h2 id=&quot;my-journey-into-advocacy&quot;&gt;My journey into advocacy&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“What you seek is seeking you.”&lt;/em&gt; - Rumi&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One day, I got an email from a founder who said he liked my LinkedIn profile and wanted me to join his organization as a Developer Advocate – that moment was surreal. That’s how I landed an internship as a Developer Advocate at Zuplo, where I learned something crucial: to understand developers’ pain points, I needed to walk in their shoes. To truly advocate for developers, I needed to be a developer myself.&lt;/p&gt;

&lt;p&gt;Wanting to experience the world of technology from the inside led me to join JPMorgan as a Software Development Engineer, where I got to experience their work firsthand, hear about their challenges, and understand their breakthroughs and victories.&lt;/p&gt;

&lt;p&gt;Each project and colleague shaped my perspective. By the end, I didn’t just understand developers, I was one of them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/vanshika-intro-blog/talks-982d3b93901bdaf48af54aae2870ecd3ca95ff0d7b6fbebd235806a8eb57b6e9.jpg&quot; alt=&quot;Vanshika working with developer communities&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;building-impact-and-recognition&quot;&gt;Building impact and recognition&lt;/h2&gt;
&lt;p&gt;Over the past few years, I delivered various sessions at leading Indian colleges, mentored over 10,000 students, and built a community of more than 600,000 tech enthusiasts. Every session sparked curiosity, every mentorship helped shape someone’s path.&lt;/p&gt;

&lt;p&gt;I collaborated with brands I admired, while enterprises, founders, and the Government of India recognized my work with a National Award. Magazines, articles, and podcasts also featured my efforts. At that moment, I knew this was my calling to make my professional life an extension of what I love doing.&lt;/p&gt;

&lt;p&gt;I remember one time I went to a college for a session where I was the only woman on a panel speaking to nearly 3,000 students. Afterwards, several women approached me, nervous, excited, and full of questions. One said, “Seeing you on that stage inspired us. If you can do it, we can too.”&lt;/p&gt;

&lt;p&gt;That moment stayed with me. I understand that this is the work that matters. These conversations remind me why I do this work every single day; it’s worth it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/vanshika-intro-blog/community-bd7cff8b6b07c9c80760997ca6f153639ee670dd9ee8b33d15183c0b495632ef.jpg&quot; alt=&quot;Community and meeting with people&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-next-chapter-at-okta&quot;&gt;The next chapter at Okta&lt;/h2&gt;
&lt;p&gt;When I saw that Okta – an organization I genuinely admired – was hiring, I applied immediately. I’m eternally grateful that I did.&lt;/p&gt;

&lt;p&gt;There is something surreal about writing this: I am joining Okta as a Senior Builder Advocate. I am writing this sentence, and my brain is doing a thing where it’s not sure if this is real life.&lt;/p&gt;

&lt;p&gt;Yes, Builder Advocate, because in today’s AI era, we don’t need to be traditional developers to create products and bring ideas to life. With the right tools and platforms, anyone can build and advocate not just for developers, but for builders of all kinds. Builders today aren’t just coders.&lt;/p&gt;

&lt;p&gt;They’re AI builders, vibecoders, LLMs/agents, no-code and low-code builders, and developers, all creating real value. Okta is here for all of us.&lt;/p&gt;

&lt;p&gt;This is the kind of role I once admired from the outside and thought, “This is where all my pieces fit.” I’m grateful to be here.&lt;/p&gt;

&lt;p&gt;I’m excited for the journey ahead. Okta’s mission to secure every interaction deeply resonates with me. I’m committed to bringing my passion for community and advocacy to this role, listening to builders, understanding their challenges, and helping them succeed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/vanshika-intro-blog/okta-d55a62ff41373baebbe3b62e82507c31cce6c68687a079039f13734ca5420dbd.jpg&quot; alt=&quot;Vanshika at Okta&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s connect. Whether you are a developer, a builder, or curious about identity and access management, I’d love to build together. That’s what Okta is about. That’s what I’m here to do. Come talk to me on &lt;a href=&quot;https://www.x.com/vanshikatech&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://www.linkedin.com/in/vanshikapandeyy&quot;&gt;LinkedIn&lt;/a&gt;, or &lt;a href=&quot;https://github.com/Vanshikapandey30&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s to new beginnings and the incredible community we’re building together at Okta.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;
</description>
        <pubDate>Thu, 20 Aug 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/08/20/vanshika-intro-blog</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/08/20/vanshika-intro-blog</guid>
      </item>
    
      <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 authorization for 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 responds only to authorized requests. In this common pattern, 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 Proof Key for Code Exchange (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 and Resources&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;In the &lt;strong&gt;Sign in Method&lt;/strong&gt; section, select &lt;strong&gt;OIDC - OpenID Connect&lt;/strong&gt;&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;Assignments&lt;/strong&gt; section, assign a group or keep the default &lt;strong&gt;Everyone&lt;/strong&gt; group assigned for all&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 doesn’t 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 displays 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. The callback then stores both the userinfo and the full token 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 displays 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 can 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 &lt;strong&gt;Login&lt;/strong&gt;. 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 &lt;strong&gt;Logout&lt;/strong&gt;. 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 your app requests this scope, Okta includes 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 attackers from replaying or misusing tokens issued for other services.&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 runs&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;Nina Patel&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;Nina&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;Patel&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;nina.patel@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 looks 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 Okta configuration 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 can 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 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;static/css/index.css&lt;/code&gt; file and add the following CSS at 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 appears, listing 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-a7068b43529b0895a27496ddb58b82686f1bfd39f5bbd47609c2fff1bb489e7a.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;&lt;a href=&quot;https://developer.okta.com/docs/guides/create-an-app-integration/saml2/main/#create-a-custom-app-integration&quot;&gt;Create a custom SAML app integration&lt;/a&gt; representing your requesting app, using these values:&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: “Requesting App”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Single sign-on URL&lt;/strong&gt;: the ACS URL of your requester 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;: the SP Entity of your requester, 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;/ul&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;On the &lt;strong&gt;Sign On&lt;/strong&gt; tab, &lt;a href=&quot;https://developer.okta.com/docs/guides/create-an-app-integration/saml2/main/#create-a-custom-app-integration&quot;&gt;retrieve your SAML setup details&lt;/a&gt;: under &lt;strong&gt;View SAML setup instructions&lt;/strong&gt;, copy the &lt;strong&gt;Identity Provider Single Sign-On URL&lt;/strong&gt; and &lt;strong&gt;Identity Provider Issuer&lt;/strong&gt;, and download the X.509 certificate as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idp-cert.pem&lt;/code&gt;. You’ll need all three in your requesting app setup.&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/provisioning/lcm/lcm-assign-app-user.htm&quot;&gt;Assign your test user&lt;/a&gt; to the app on the &lt;strong&gt;Assignments&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;Now that we have the requesting app registered, it’s time to &lt;a href=&quot;https://developer.okta.com/docs/guides/create-an-app-integration/saml2/main/#create-a-custom-app-integration&quot;&gt;create a custom SAML app integration&lt;/a&gt; representing the resource app, using these values:&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;General Settings&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;App name&lt;/strong&gt;: “Resource App for Testing”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;strong&gt;Configure SAML&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Single sign-on URL&lt;/strong&gt;: “https://idp.xaa.dev/saml-resource/acs”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Audience URI (SP Entity ID)&lt;/strong&gt;: “https://idp.xaa.dev/saml-resource/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;/ul&gt;

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

  &lt;p&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;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;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#enable-xaa-on-a-custom-app-integration&quot;&gt;Enable Cross App Access (XAA)&lt;/a&gt; on the resource app integration from the &lt;strong&gt;Resource Server&lt;/strong&gt; tab, and configure:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&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;&lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/provisioning/lcm/lcm-assign-app-user.htm&quot;&gt;Assign your test user&lt;/a&gt; to the app on the &lt;strong&gt;Assignments&lt;/strong&gt; tab.&lt;/p&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; and use these values:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;IdP issuer URL&lt;/strong&gt;: your Okta Integrator account ID (i.e., https://your-okta-domain.okta.com)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test user identifier&lt;/strong&gt;: your email, e.g., name1234…@okta.com&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SAML IdP entityID&lt;/strong&gt;: from the requesting app’s &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;, the &lt;strong&gt;Identity Provider Issuer&lt;/strong&gt; value (i.e., http://www.okta.com/&lt;app-id&gt;)&lt;/app-id&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After all values are entered, click &lt;strong&gt;Register&lt;/strong&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 created and the resource application, which in this case is xaa.dev. You configure credentials, link your requesting app during registration, 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;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#register-ai-agent-user-access-and-authentication&quot;&gt;register the AI Agent and link your requesting app&lt;/a&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;Under &lt;strong&gt;Profile&lt;/strong&gt;, enter a &lt;strong&gt;Name&lt;/strong&gt;, e.g., “Agent”, and an optional description, then press &lt;strong&gt;Next&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Under &lt;strong&gt;User access and authentication &amp;gt; Allow users to access this agent&lt;/strong&gt;, select &lt;strong&gt;Select an existing app&lt;/strong&gt;, then choose the Okta SAML requesting app you created earlier (e.g., “Requesting App”). This app acts as the requesting app for the XAA flow: your users sign in to the agentic app through it, and the agent then acts on their behalf. Press &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Under &lt;strong&gt;owners&lt;/strong&gt;: assign your test user as an individual owner&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;Client registration&lt;/strong&gt; tab, &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#register-ai-agent-user-access-and-authentication&quot;&gt;choose a client registration method&lt;/a&gt;:
    &lt;ol&gt;
      &lt;li&gt;Select the &lt;strong&gt;Public/private key&lt;/strong&gt; section&lt;/li&gt;
      &lt;li&gt;In the &lt;strong&gt;Define where keys are managed&lt;/strong&gt; section, choose &lt;strong&gt;Okta&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;Under the &lt;strong&gt;Add and manage keys&lt;/strong&gt; section, select &lt;strong&gt;Add public key&lt;/strong&gt;, then &lt;strong&gt;Generate new key&lt;/strong&gt;. Under &lt;strong&gt;PEM&lt;/strong&gt;, copy the private key into the &lt;strong&gt;Private key (PKCS8 PEM or private JWK)&lt;/strong&gt; field in your requesting app&lt;/li&gt;
      &lt;li&gt;Copy the &lt;strong&gt;KEY ID&lt;/strong&gt; and add it back to your requesting app&lt;/li&gt;
      &lt;li&gt;In the &lt;strong&gt;Provide Client ID to AI agent builder or developer&lt;/strong&gt; step, copy the &lt;strong&gt;Client ID&lt;/strong&gt;, and add it back to your requesting app&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;User access&lt;/strong&gt; tab
    &lt;ol&gt;
      &lt;li&gt;In the &lt;strong&gt;App used for access configuration&lt;/strong&gt;, select &lt;strong&gt;Select an existing SAML app&lt;/strong&gt;, then your &lt;strong&gt;SAML Application&lt;/strong&gt;, e.g., “Requesting App”&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;On the &lt;strong&gt;Resource connections&lt;/strong&gt; tab, &lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#configure-the-xaa-connection&quot;&gt;add a resource connection to the AI Agent&lt;/a&gt;. Use these values:
    &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Application instance&lt;/strong&gt;: your resource app (e.g., “Resource App for Testing”)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;AI agent’s client ID registered in this app&lt;/strong&gt;: the &lt;strong&gt;Client ID&lt;/strong&gt; from xaa.dev (it should look similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byora...&lt;/code&gt;)&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Scopes&lt;/strong&gt;: &lt;strong&gt;Allow any scope&lt;/strong&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://developer.okta.com/docs/guides/xaa-agent-to-app/main/#activate-the-ai-agent&quot;&gt;Activate the AI Agent&lt;/a&gt;: activating the linked requesting app integration usually activates the AI Agent. If the agent’s status is &lt;strong&gt;STAGED&lt;/strong&gt;, go to the &lt;strong&gt;Actions&lt;/strong&gt; drop-down menu at the top and select &lt;strong&gt;Activate&lt;/strong&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;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;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-saml-requester/conformance-log-39e73c8a2b3b9fa66bd0a2ea098374dcd1602da53c6b5f06c4cd1db364626dba.jpg&quot; alt=&quot;conformance log&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&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;Register your requesting application at &lt;a href=&quot;https://xaa.dev/developer/register?tab=oidc&quot;&gt;xaa.dev&lt;/a&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>
    
  </channel>
</rss>
