<?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>Okta Developer Connect San Francisco 2026 Recap</title>
        <description>&lt;p&gt;“Building an agent is only half the battle. Governing it is where we get stuck.”&lt;/p&gt;

&lt;p&gt;That question came up in nearly every conversation we had with engineering managers leading up to &lt;strong&gt;&lt;a href=&quot;https://luma.com/v2tmx6bf?tk=D5BfTm&quot;&gt;Okta Developer Connect San Francisco&lt;/a&gt;&lt;/strong&gt;. The second edition of our flagship developer event series brought more than 100 developers, architects, founders, platform engineers, and security leaders to Okta HQ on April 30 for an afternoon of technical sessions, hands-on labs, and community conversations on one theme: securing identity in the age of AI.&lt;/p&gt;

&lt;h2 id=&quot;unlocking-ai-in-the-enterprise-okta-for-ai-agents-is-ga&quot;&gt;Unlocking AI in the enterprise: Okta for AI Agents is GA&lt;/h2&gt;

&lt;p&gt;The opening keynote made a point most enterprise teams are now grappling with. Identity has always been a security control. With AI agents in the picture, identity also becomes the governance layer for a class of workloads that didn’t exist a year ago: non-human, autonomous, and increasingly trusted to act on behalf of users.&lt;/p&gt;

&lt;p&gt;The keynote also marked the &lt;strong&gt;&lt;a href=&quot;https://www.okta.com/blog/ai/okta-for-ai-agents-general-availability&quot;&gt;general availability of Okta for AI Agents&lt;/a&gt;&lt;/strong&gt;. The announcement framed the problem in terms that every security leader recognizes: once an agent ships, three questions arise almost immediately, and the product answers each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are my agents?&lt;/strong&gt; Discovery and onboarding work across frameworks, clouds, and SaaS environments, with shadow AI detection for the agents nobody officially registers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can they connect to?&lt;/strong&gt; Protection rests on short-lived credentials, scoped tokens, vaulted secrets, and access controls that extend to Model Context Protocol (MCP) servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can they do?&lt;/strong&gt; Governance covers the full lifecycle: access requests, certification, audit logs that stream to Security Information and Event Management (SIEM) platforms, and a kill switch that deactivates an agent that goes off-script.&lt;/p&gt;

&lt;p&gt;For the developers and architects in the room, the announcement set the tone for the rest of the day. Every session that followed answered one of those three questions in some way. You can dig deeper on the &lt;strong&gt;&lt;a href=&quot;https://www.okta.com/products/govern-ai-agent-identity/&quot;&gt;Okta for AI Agents product page&lt;/a&gt;&lt;/strong&gt; or in the &lt;strong&gt;&lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/ai-agents/ai-agents-home.htm&quot;&gt;documentation&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;why-this-conversation-matters-now&quot;&gt;Why this conversation matters now&lt;/h2&gt;

&lt;p&gt;Across the sessions and the AI Interview activation we ran throughout the event, one theme kept coming up: agents are arriving in production faster than most teams can govern them.&lt;/p&gt;

&lt;p&gt;Developers told us they have shifted from writing every line of code to reviewing, guiding, and orchestrating AI-generated work. Engineering teams are connecting agents to internal APIs, retrieval-augmented generation (RAG) pipelines, MCP servers, and third-party tools. Security and IAM leaders are watching it happen and asking how to apply the principles they already trust (least privilege, scoped access, auditability) to identities they didn’t anticipate when they built those controls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few patterns stood out from those conversations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agents are outpacing governance.&lt;/strong&gt; Many teams ship an agent in days. Wrapping it in policy, access reviews, and lifecycle controls takes weeks or months. The gap between “it works” and “it’s safe to leave running” is where most of the risk lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static credentials are the new shadow IT.&lt;/strong&gt; Hardcoded keys, long-lived tokens, and shared service accounts are how a lot of agents reach data today. They make the demo work. They also make incidents harder to scope when something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RAG has a permissions problem.&lt;/strong&gt; Retrieval-augmented generation is now the default pattern for enterprise AI. The hard part is making sure an agent retrieving documents on behalf of a user only sees what that user can access at the data layer, and not just the prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-human identity is a real category.&lt;/strong&gt; Teams that already invested in human identity governance now realize they need an equivalent practice for agents, bots, and workloads. Naming them, owning them, and decommissioning them are no longer optional.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/okta-developer-connect-sf-recap/speakers-odc-sf-53bc09c18611797992f4429f0012fb1b495dce757ecbbd152b4b9de5361acac4.jpg&quot; alt=&quot;speakers-odc-sf&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;cross-app-access-and-the-protocol-behind-it&quot;&gt;Cross App Access and the protocol behind it&lt;/h2&gt;

&lt;p&gt;Cross App Access (XAA) drew one of the most-attended sessions of the day, and for good reason. As agents move between applications, the consent model designed for users clicking buttons doesn’t hold up. Repeated prompts get fatigue clicks. Unmanaged app-to-app connections become invisible to security teams. Long-lived tokens leak.&lt;/p&gt;

&lt;p&gt;XAA introduces a model in which the enterprise identity provider mediates access between applications based on policy, rather than relying on per-user consent or app-to-app integrations. The session grounded the protocol in the OAuth context most developers already know, which made the new pieces (identity assertion and token exchange across app boundaries) easier to place.&lt;/p&gt;

&lt;p&gt;For developers, the practical takeaway is clear. If you’re building an application that needs to act on behalf of a user inside another application, the question is no longer “how do I get a token?” It’s “how does my organization want to govern this access, and how does my app respect that policy?”&lt;/p&gt;

&lt;p&gt;A hands-on walkthrough of &lt;strong&gt;&lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt;&lt;/strong&gt; gave attendees a chance to step through the Cross App Access flow on their own laptops. Watching identity assertion and token exchange move across a requesting app, an identity provider, and a resource app turned an otherwise dense protocol into something concrete. It gave the room a working mental model to take back to their own architectures.&lt;/p&gt;

&lt;h2 id=&quot;securing-ai-agents-with-auth0&quot;&gt;Securing AI agents with Auth0&lt;/h2&gt;

&lt;p&gt;A dedicated session walked through the identity patterns developers can adopt today when building AI applications. The framing stayed deliberately practical, organized around the four risks that show up most often when an agent starts touching real data: authenticating the user the agent acts for so context stays in the chain; using a Token Vault so agents don’t handle long-lived third-party credentials directly; asynchronous authorization so users can approve sensitive actions out of band; and fine-grained authorization so an agent only retrieves the data and triggers the actions a user can access.&lt;/p&gt;

&lt;p&gt;The session landed because these patterns map naturally to OAuth concepts most developers already know. The work isn’t learning a new identity model from scratch. It’s applying delegation, scoped tokens, and policy-driven authorization to a new kind of caller, one that doesn’t have a browser, doesn’t click “allow,” and doesn’t stop to think before acting. Explore the patterns and SDKs in &lt;strong&gt;&lt;a href=&quot;https://auth0.com/ai&quot;&gt;Auth0 for AI Agents&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-panel-what-teams-are-actually-asking-about-ai-agents&quot;&gt;The panel: What teams are actually asking about AI agents&lt;/h2&gt;

&lt;p&gt;The panel pulled the conversation out of theory and into the questions teams are wrestling with right now. An opening icebreaker about what an unsecured personal AI agent might buy if it went rogue set the tone: practical, direct, and sometimes intentionally playful.&lt;/p&gt;

&lt;p&gt;When applications already have their own access controls, does identity get the final say, or does the application remain the real gatekeeper? How do you stop MCP servers from quietly becoming a backdoor into enterprise data? Which authentication mistakes do developers most often make when they build their first agent? How do teams move from identity as an audit log they review after the fact to identity as a guardrail that enforces decisions in real time?&lt;/p&gt;

&lt;p&gt;The panel didn’t offer easy answers, and that was the point. Securing AI agents isn’t a feature you add at the end of a build. It’s a set of decisions about delegation, consent, and least privilege you need to make early, before you connect the agent to anything that matters.&lt;/p&gt;

&lt;p&gt;For developers, the practical takeaway is clear. If you’re building an application that needs to act on behalf of a user inside another application, the question is no longer “how do I get a token?” It’s “how does my organization want to govern this access, and how does my app respect that policy?”&lt;/p&gt;

&lt;h2 id=&quot;what-this-means-for-builders&quot;&gt;What this means for builders&lt;/h2&gt;

&lt;p&gt;A few takeaways from the day are worth carrying forward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat agents as identities, not features.&lt;/strong&gt; The moment an agent can act on data or trigger workflows, it needs the same lifecycle treatment as a user account: ownership, scope, review, and revocation. Naming it after the application isn’t enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design the consent model before the integration.&lt;/strong&gt; It’s much easier to decide who can delegate what, and how that delegation expires, before you connect an agent to a calendar, a CRM, or a document store. Once the integration goes live, every change becomes a migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Push authorization closer to the data.&lt;/strong&gt; Fine-grained authorization works best at the layer where decisions actually happen: the database, the vector store, the API. Token-level scopes alone don’t stop a RAG pipeline from surfacing documents outside the user’s permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan for the agent you haven’t built yet.&lt;/strong&gt; Most teams plan to deploy more agents next year than they did this year. The patterns that feel optional today - discovery, governance, scoped credentials become foundational the moment you have a fleet to manage.&lt;/p&gt;

&lt;h2 id=&quot;community-conversations-and-the-ai-interview-activation&quot;&gt;Community, Conversations, and the AI interview activation&lt;/h2&gt;

&lt;p&gt;Beyond the sessions, the energy in the room was the hardest part to put on a slide. Attendees stayed for the hands-on lab, joined the AI Interview activation, asked sharp questions during the panel, and kept the conversation going with Okta and Auth0 engineers, product leaders, and developer advocates well past the formal close.&lt;/p&gt;

&lt;p&gt;The AI Interview activation in particular created a useful feedback loop. Developers told us how AI is reshaping their day, not in marketing terms, but in the specifics of how they review pull requests, find documentation, and decide when to trust an AI-generated answer. Those conversations are already shaping what we build and what we write next.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/okta-developer-connect-sf-recap/community-networking-e4a61b4def6fe5ea4c060428dcc9615fa4e97518aec7915bc71115ef3dac97cf.jpg&quot; alt=&quot;community&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-comes-next&quot;&gt;What comes next&lt;/h2&gt;

&lt;p&gt;Okta Developer Connect started as a forum for the conversations that are harder to have on a webinar, the ones where developers and architects can push back, ask the awkward question, and leave with something they can actually use. The San Francisco edition continued that mission with a sharper focus on AI agents, OAuth, Cross App Access, and the identity patterns shaping the next generation of applications.&lt;/p&gt;

&lt;p&gt;If a single thread ran through the day, it was this: the teams that treat identity as part of the agent, not a wrapper around it, ship at the speed the business asks for, without trading away the controls that keep them safe.&lt;/p&gt;

&lt;p&gt;Thank you to everyone who joined us in San Francisco, asked the harder questions, and shared what you’re building. We’re looking forward to the next edition.&lt;/p&gt;

&lt;p&gt;Stay tuned for upcoming Okta Developer Connect events, and follow OktaDev 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 &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube&lt;/a&gt; for new tutorials, videos, and announcements.&lt;/p&gt;
</description>
        <pubDate>Tue, 09 Jun 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/06/09/okta-developer-connect-sf-recap</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/06/09/okta-developer-connect-sf-recap</guid>
      </item>
    
      <item>
        <title>The One Where I Found My Way to DevRel</title>
        <description>&lt;p&gt;If you’ve watched Friends, you’ll know that life rarely goes according to plan. One day, you’re helping a friend move a couch while yelling “Pivot!”, and the next, you’re wondering how you ended up there in the first place.&lt;/p&gt;

&lt;p&gt;My career has felt a little like that.&lt;/p&gt;

&lt;p&gt;Growing up, I was always drawn to creative pursuits. You could usually find me with a book in hand, experimenting with calligraphy, sketching something random, writing poetry, baking a new dessert, or trying out a recipe I found somewhere online. Technology wasn’t the obvious destination (even though I love building!), storytelling was.&lt;/p&gt;

&lt;p&gt;That love for explaining things eventually led me into technical writing.&lt;/p&gt;

&lt;h2 id=&quot;building-docs-for-developers&quot;&gt;Building docs for developers&lt;/h2&gt;

&lt;p&gt;I started working with startups, creating everything from API documentation and user guides to onboarding content and developer-facing resources. I loved the challenge of taking something complex and making it easier to understand. There was something deeply satisfying about helping someone go from “What does this even mean?” to “Oh, now I get it.”&lt;/p&gt;

&lt;p&gt;For a while, I thought documentation was the destination. Plot twist! It wasn’t.&lt;/p&gt;

&lt;p&gt;When I joined JDoodle (a web-based IDE), I found myself spending more time talking to developers. I wasn’t just writing for them anymore. I was listening to them, learning how they built things, understanding their frustrations, and seeing firsthand what made them excited about technology.&lt;/p&gt;

&lt;p&gt;The more conversations I had, the more I realized something. The thing I enjoyed most wasn’t just creating documentation. It was connecting with people.&lt;/p&gt;

&lt;h2 id=&quot;finding-my-place-in-devrel&quot;&gt;Finding my place in devrel&lt;/h2&gt;

&lt;p&gt;I started attending developer events and conferences. Then came product demos. Then booth duty. Then community conversations. Then hackathons. Then organizing internal events.&lt;/p&gt;

&lt;p&gt;Somewhere along the way, what started as “I need to understand developers better so I can write better documentation” turned into “I genuinely love being part of developer communities.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/zahwah-jameel-intro/community-events-2a08d356e7ad3396ad6a352a8b5be569f233e82db59f6b7c00b9326eb972fc1e.jpg&quot; alt=&quot;Zahwah at community events&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I discovered that my favorite moments weren’t necessarily publishing a new document or updating an API reference.&lt;/p&gt;

&lt;p&gt;They were moments like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Watching a developer finally solve a problem after a conversation.&lt;/li&gt;
  &lt;li&gt;Seeing people get excited during a product demo.&lt;/li&gt;
  &lt;li&gt;Helping organize hackathons where creativity and technology collide.&lt;/li&gt;
  &lt;li&gt;Meeting builders who were passionate about bringing their ideas to life.&lt;/li&gt;
  &lt;li&gt;Learning something new from every event I attended.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As it turns out, the skills that drew me to writing in the first place translated surprisingly well into developer advocacy.&lt;/p&gt;

&lt;p&gt;At their core, documentation and developer advocacy are both about empathy. They require understanding your audience, telling clear stories, and helping people succeed.&lt;/p&gt;

&lt;h2 id=&quot;joining-the-okta-community&quot;&gt;Joining the Okta community&lt;/h2&gt;

&lt;p&gt;Looking back, it makes perfect sense. The person who loved books, writing, art, and storytelling didn’t leave those interests behind when entering tech. She simply found a new audience.&lt;/p&gt;

&lt;p&gt;Today, I’m incredibly excited to continue that journey at Okta.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/zahwah-jameel-intro/office-photo-ac268e88867bee74b0eb17d8ee6b0a265be43e7bc0c09c86721079dd5d1f861c.jpg&quot; alt=&quot;Zahwah at Okta&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’ll be working alongside developers, creators, builders, and community members who are shaping the future of identity and security. I’m looking forward to learning, sharing, creating content, attending events, and most importantly, connecting with the people who make this community so special.&lt;/p&gt;

&lt;p&gt;As Phil Dunphy said, &lt;em&gt;“When life gives you lemonade, make lemons. Life will be all like, ‘What?!’”&lt;/em&gt; My career may not have followed a traditional path, but somehow it led me exactly where I wanted to be.&lt;/p&gt;

&lt;p&gt;I’m excited to meet all of you. If you see me at an event, or find me on socials, come say hello. I’d love to hear what you’re building.&lt;/p&gt;
</description>
        <pubDate>Fri, 05 Jun 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/06/05/zahwah-intro-blog</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/06/05/zahwah-intro-blog</guid>
      </item>
    
      <item>
        <title>How to Build Low-Code API Integrations for Enterprise Apps Using Okta</title>
        <description>&lt;p&gt;API Integration Actions are now available in Okta Integration Network (OIN) for Integrator Free Trial Orgs to build Provisioning, Entitlements, and Universal Logout applications.&lt;/p&gt;

&lt;h2 id=&quot;what-are-api-integration-actions&quot;&gt;What are API Integration Actions?&lt;/h2&gt;

&lt;p&gt;API Integration Actions are a feature that uses Workflows, Okta’s low-code builder, to enable independent software vendors (ISVs) to build Okta Integrations (Provisioning, Entitlements, Universal Logout) that are seamlessly invoked by Okta services — for example, retrieving and updating entitlements or triggering risk-based logout flows.&lt;/p&gt;

&lt;p&gt;You can just skip the complexity of building and maintaining a System for Cross-domain Identity Management (SCIM) server. API Integration Actions allow you to use your existing APIs as-is by mapping them directly to Okta action contracts. By using our low-code builder, you no longer need in-depth knowledge of protocols, making it faster and easier to build, test, and deliver enterprise-grade Secure Identity Integrations. This leads to a fast time-to-value for customers leveraging ISV data for connector-heavy Okta Identity Governance (OIG) use cases.&lt;/p&gt;

&lt;h2 id=&quot;benefits-of-low-code-api-integration-for-isvs&quot;&gt;Benefits of low-code API integration for ISVs&lt;/h2&gt;

&lt;p&gt;For the ISV application developer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Built on Workflows: use the low-code builder instead of writing and maintaining complex code&lt;/li&gt;
  &lt;li&gt;Translates your API calls into formats consumable by Okta: bring your APIs as they are, without having to make any changes&lt;/li&gt;
  &lt;li&gt;No need for in-depth knowledge of protocols: Workflows makes mapping your API to Okta’s format simple&lt;/li&gt;
  &lt;li&gt;No need to invest in costly infrastructure: don’t worry about managing a SCIM server&lt;/li&gt;
  &lt;li&gt;It’s not just secure — it’s fast and easy!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;how-to-build-low-code-api-integrations-with-okta-workflows&quot;&gt;How to build low-code API integrations with Okta Workflows&lt;/h2&gt;

&lt;p&gt;If you don’t already have an account, sign up for an &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Okta Integrator Free Plan&lt;/a&gt; first. Once created, log in and follow these steps.&lt;/p&gt;

&lt;h3 id=&quot;step-1-create-your-oin-integration&quot;&gt;Step 1: Create your OIN integration&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Click &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Your OIN Integrations&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Build new OIN integration&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Choose the single sign-on (SSO) type&lt;/li&gt;
  &lt;li&gt;If you are building an integration that uses Universal Logout, choose that option. If you are building an integration using provisioning and entitlements, choose those options&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;View integration details&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/add-integration-capabilities-de356175390fd0bb655700f24f6d620de643de30b5ee19a123b0c72035d4d184.jpg&quot; alt=&quot;Add integration capabilities screen showing Session Lifecycle Management and Identity Lifecycle Management options&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add the integration details&lt;/li&gt;
  &lt;li&gt;If you are a customer creating an integration for your orgs during the EA period, put “Customer-created integration - not for the public catalog” in the description field. Then provide a list of your org tenant IDs and subdomains. After submission, you will need to email your account manager to ensure this integration is deployed to your orgs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/catalog-props-1-42da508ba461b07218328e6a95a110860eaaf2c537fb85c1cf021a5f0b70d552.jpg&quot; alt=&quot;OIN catalog properties form showing display name, description, and logo upload fields&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/catalog-prop-2-cefa62a0ba05fbcd6a882897a2c8b43d90a81c560aa7fffb1e7d0cd1e993b5f7.jpg&quot; alt=&quot;OIN catalog properties form continued showing support contact information and use case options&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;step-2-configure-authentication-and-api-integration-actions&quot;&gt;Step 2: Configure authentication and API Integration Actions&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Tenant settings refer to subdomains or additional information needed for the SSO components&lt;/li&gt;
  &lt;li&gt;Authentication settings include all of the allowed integration types. Choose the one used by the API and provide the information&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Save and start building&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/tenant-auth-3f0d2a1419a2b4ef969758b56f75c918d1bd420be3c811b49eb4ae92c98c017f.jpg&quot; alt=&quot;Tenant settings and authentication settings screens showing label, name fields, and OAuth 2 configuration with authorize and token endpoints&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;This will send you to &lt;strong&gt;Integration Builder&lt;/strong&gt; within the Okta Workflows product, where you build out the flows that connect to the API&lt;/li&gt;
  &lt;li&gt;Validate that the information is correct — it should match what was provided in OIN Wizard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/wf-project-7ac612cdc7ceaa860dfb568367bfb216e1de464b396c32d4c952e7ac849c0c67.jpg&quot; alt=&quot;Integration Builder project screen showing General, Authentication, Test connection, and API Spec tabs&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click on the &lt;strong&gt;Authentication&lt;/strong&gt; tab and add the authentication information. Make sure it matches what is in the OIN Wizard&lt;/li&gt;
  &lt;li&gt;Fill out the &lt;strong&gt;Authentication Mapping&lt;/strong&gt; section to map the OIN Wizard auth parameters to the Workflows auth parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/auth-mapping-4abb7154894f018550d250e7db054f05ba6f4838ae2878de761dc4a31fc8866d.jpg&quot; alt=&quot;Authentication mapping screen showing connection parameters mapped to OIN app integration variables&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click on &lt;strong&gt;New Component&lt;/strong&gt; and choose &lt;strong&gt;Add Action&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Choose the API Integration Action component from the list, and click &lt;strong&gt;save&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/choose-action-d397cabbed4ab58e70c8c4602d677211c989957b6fc984d83cb1d3a467281676.jpg&quot; alt=&quot;Add new action dialog showing API integration action component options, including Provisioning action contracts&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;step-3-build-your-low-code-workflow-flows&quot;&gt;Step 3: Build your low-code workflow flows&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Click on &lt;strong&gt;New Flow&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Create the workflow and repeat as necessary&lt;/li&gt;
  &lt;li&gt;Once your flows are created, you can create test flows in the test folder to validate that the API calls are being made correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/flow-list-982e6ea3cea32eefc6d8acc0d4aadc097b6127311476704d4155c4a835bdff6d.jpg&quot; alt=&quot;Provisioning action contracts screen showing App Event flows for List users, Get group by id, List groups, and more&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;After testing, click on &lt;strong&gt;Validate and Submit&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Click on &lt;strong&gt;Validate flows&lt;/strong&gt; and fix any errors that may exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/validate-flows-9577e21a89b466cc9a6d70bdd13f924af10ec0f170ed58a3f3a1249a4d99819e.jpg&quot; alt=&quot;Validate and submit flows screen showing flow validation status and Continue submission in OIN button&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Click on &lt;strong&gt;Continue submission in OIN&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Back in the OIN Wizard, choose the correct flows for each of the API Integration Actions that have been created&lt;/li&gt;
  &lt;li&gt;Click on &lt;strong&gt;Get started with testing&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/configure-integration-d4c0e694213b005d4d8798ff96ed30ed6a886b738af0419dd39cac988c4f86e7.jpg&quot; alt=&quot;Provisioning API Integration Actions screen showing User query, User Schema Discovery, and User Operations flow mapping&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-test-your-api-integration-before-publishing-to-the-oin&quot;&gt;How to test your API integration before publishing to the OIN&lt;/h2&gt;

&lt;p&gt;Before submitting your integration for review and publication, you must test it in your Okta org. Your integration will only be available on your Okta org. Okta admins will see the same authorization experience.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Provide the testing information needed for Okta to review the submission&lt;/li&gt;
  &lt;li&gt;Once finished, click on &lt;strong&gt;Test your integration&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/test-instance-da421c1d820fd16dfe7261b7c836a97665d5e5d37ef1dafd05a219d2794c2bc3.jpg&quot; alt=&quot;Test your integration screen showing test account fields, account URL, username, password, and testing instructions&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;create-a-test-instance&quot;&gt;Create a test instance&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Fill out the information, including the test account and any SSO testing features on the &lt;strong&gt;Test your integration&lt;/strong&gt; section of the OIN Wizard&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Test your integration&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Follow the instructions in the &lt;strong&gt;Test integration&lt;/strong&gt; section to generate a test instance and complete all of the testing&lt;/li&gt;
  &lt;li&gt;Validate your flows by clicking the button — take action on any failures that occur&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/low-code-api-integration/test-integration-2273cb1e49820cf0c061b6806c10e1f95bd7d7de80b3f0d1eefacd01f79ed15a.jpg&quot; alt=&quot;Test integration screen showing app instances for testing with SAML SSO instance detected and Provisioning and Entitlement instances pending&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;update-a-test-instance&quot;&gt;Update a test instance&lt;/h3&gt;

&lt;p&gt;When you make an update to your submission in the OIN Manager (for example, modifying the scopes or name of the integration), the update will not automatically be reflected in your test instance for security reasons.&lt;/p&gt;

&lt;p&gt;To update a test instance, repeat the procedure above for creating a test instance.&lt;/p&gt;

&lt;p&gt;Once finished, click the last checkbox to enable submitting your integration for review. This process is similar to the existing OIN Catalog process.&lt;/p&gt;

&lt;h2 id=&quot;get-started-with-low-code-api-integration-using-okta&quot;&gt;Get started with low-code API integration using Okta&lt;/h2&gt;

&lt;p&gt;If you’re ready to build an integration between your APIs and Okta’s, start by exploring how to build and publish an application using API Integration Actions to the OIN by reading our product documentation &lt;a href=&quot;/docs/guides/build-api-actions/main/&quot;&gt;Build and publish API Integration Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube 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, 12 May 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/05/12/low-code-api-integration</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/05/12/low-code-api-integration</guid>
      </item>
    
      <item>
        <title>Develop a XAA-Enabled Resource Application and Test with Okta</title>
        <description>&lt;p&gt;From an enterprise resource app owner’s perspective, Cross App Access (XAA) is a game-changer because it allows their resources to be “AI-ready” without compromising on security. In the XAA model, resource apps rely on the enterprise’s Identity Provider (IdP) to manage access. Instead of building out interactive OAuth flows, they defer to the IdP to check enterprise policies and user groups, assign AI agent permissions, and log and audit AI agent requests as they occur. In return, the app’s OAuth server needs only to perform a few checks:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;When the app’s OAuth server receives a POST request to its token endpoint from an AI agent, the app fetches the IdP’s public keys (via the JWKS endpoint) to ensure the ID-JAG token attached to the request was actually minted by the trusted company IdP.&lt;/li&gt;
  &lt;li&gt;It confirms the token was intended for this app specifically. If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt; claim doesn’t match the app’s own identifier, it rejects the request.&lt;/li&gt;
  &lt;li&gt;Finally, it checks the end user ID in the token’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim to know whose data to look up in your database. It must map to the same IdP identity. It will reject the request if the user isn’t recognized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read in depth about XAA to better understand how this works and examine the token exchange flow.&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/2025/06/23/enterprise-ai&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Integrate Your Enterprise AI Tools with Cross-App Access&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Manage user and non-human identities, including AI in the enterprise with Cross App Access&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/semona-igama/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-semona-igama-03eb4c28aca3765f862b574e032d32f6f8186d04ae9f0db75bed9c74f48a9a3f.jpg&quot; alt=&quot;avatar-avatar-semona-igama.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/semona-igama/&quot;&gt;Semona Igama&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;p&gt;Or watch the video about Cross App Access:&lt;/p&gt;

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

&lt;p&gt;In this tutorial, we’ll demonstrate how to test that an XAA-enabled resource app you have created (&lt;strong&gt;TaskFlow&lt;/strong&gt;) is correctly using Okta as an &lt;strong&gt;enterprise Identity Provider (IdP)&lt;/strong&gt; to sign users in, and we’ll demonstrate how a sample AI app (&lt;strong&gt;Agent0&lt;/strong&gt;) uses XAA to get access to TaskFlow. To do this, you’ll:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Enable Cross App Access in your Okta org&lt;/li&gt;
  &lt;li&gt;Register and configure the resource app (TaskFlow) in your org&lt;/li&gt;
  &lt;li&gt;Register the requesting app (Agent0) in your org as a known XAA app and connect it to TaskFlow.&lt;/li&gt;
  &lt;li&gt;Test that the XAA flow is working correctly when Agent0 requests access to TaskFlow.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note that the apps (TaskFlow or Agent0) do not use Okta as their authorization server.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;enable-cross-app-access-in-your-okta-org&quot;&gt;Enable Cross App Access in your Okta org&lt;/h1&gt;

&lt;p&gt;To register your resource app with Okta, and set up secure agent-to-app connections, you’ll need an Okta Developer org enabled with XAA:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If you don’t already have an account, sign up for a new one here: &lt;a href=&quot;https://developer.okta.com/signup&quot;&gt;Okta Integrator Free Plan&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Once created, sign in to your new Integrator Free Plan org&lt;/li&gt;
  &lt;li&gt;In the Okta Admin Console, select &lt;strong&gt;Settings &amp;gt; Features&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Navigate to &lt;strong&gt;Early access&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Find &lt;strong&gt;Cross App Access&lt;/strong&gt; and select &lt;strong&gt;Turn on&lt;/strong&gt; (enable the toggle)&lt;/li&gt;
  &lt;li&gt;Refresh the Admin Console&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: Cross App Access is currently a self-service Early Access (EA) feature. You must enable it through the Admin Console before the apps appear in the catalog. If you don’t see the option right away, refresh and confirm you have the necessary admin permissions. Learn more in the &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/security/manage-ea-and-beta-features.htm&quot;&gt;Okta documentation on managing EA and beta features&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-resource-app/image3-c9a95bb9918d5d631678622b2343d7776e314875e72ecd70fea836d264d8164c.jpg&quot; alt=&quot; &quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;register-your-requesting-app-agent0&quot;&gt;Register your requesting app (Agent0)&lt;/h1&gt;

&lt;p&gt;To test whether your resource app is working correctly, Okta provides a placeholder entry in the Okta Integration Network catalog. It is called &lt;strong&gt;&lt;em&gt;Agent0 - Cross App Access (XAA) Sample Requesting App&lt;/em&gt;&lt;/strong&gt;. Add this to your org’s integrations.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Still in Admin Console, go to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Browse App Catalog&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for “Agent0 - Cross App Access (XAA) Sample Requesting App”, and select it&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Add Integration&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now to configure it correctly. First, assign user access to Agent0.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Change the &lt;strong&gt;Application&lt;/strong&gt; label if required, and select &lt;strong&gt;Done&lt;/strong&gt;,&lt;/li&gt;
  &lt;li&gt;Select the Assignments tab
    &lt;ul&gt;
      &lt;li&gt;To assign it to a single user, select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt; and choose your user&lt;/li&gt;
      &lt;li&gt;To assign it to a user group, select &lt;strong&gt;Assign &amp;gt; Assign to Groups&lt;/strong&gt; and choose your user group&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Click Done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, configure Agent0 with the redirect URI you will use to test Agent0&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Select the &lt;strong&gt;Sign On tab&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Edit&lt;/strong&gt;, and locate the Advanced Sign-on Settings section.&lt;/li&gt;
  &lt;li&gt;Set the &lt;strong&gt;Redirect URI&lt;/strong&gt; to the URL that your app will use. For example, &lt;a href=&quot;http://localhost:8080/redirect&quot;&gt;http://localhost:8080/redirect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Click Save.&lt;/li&gt;
  &lt;li&gt;Locate and copy the Client ID and Client secret in the Sign-On methods section. Your app must use these when signing users in through Okta.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: Only the org authorization server can be used to exchange ID-JAG tokens. Ensure you are using the org authorization server and not an Okta “custom authorization server”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-resource-app/image2-ad5eed8a0e6b495caa26809fb24390efdd64204ac8c638c84d249a028882537b.jpg&quot; alt=&quot; &quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;get-a-xaa-client-id-for-agent0-from-the-resource-apps-auth-server&quot;&gt;Get a (XAA) Client ID for Agent0 from the Resource app’s Auth Server&lt;/h2&gt;

&lt;p&gt;To allow the exchange of an ID-JAG token between Agent0 and your resource app, Agent0 must be registered as an OAuth client in your resource app’s OAuth server.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Register your requesting app (&lt;strong&gt;Agent0&lt;/strong&gt;) as an OAuth client in your resource app’s OAuth server.&lt;/li&gt;
  &lt;li&gt;Make a note of the Client ID for your requesting app (&lt;strong&gt;Agent0&lt;/strong&gt;). You’ll need this as you set up your resource app.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: The process for registering a client ID from your resource app’s OAuth server will vary depending on the product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;set-up-your-resource-app-taskflow&quot;&gt;Set up your resource app (TaskFlow)&lt;/h1&gt;

&lt;p&gt;To set up your resource app in your org, you can use the placeholder integration in the OIN catalog called &lt;strong&gt;&lt;em&gt;Todo0 - Cross App Access (XAA) Sample Resource App&lt;/em&gt;&lt;/strong&gt; and configure it as your resource app.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Still in Admin Console, navigate to &lt;strong&gt;Applications &amp;gt; Applications&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Browse App Catalog&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Search for &lt;strong&gt;Todo0 - Cross App Access (XAA) Sample Resource App&lt;/strong&gt;, and select it&lt;/li&gt;
  &lt;li&gt;Select &lt;strong&gt;Add Integration&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now give it a helpful name and assign user access to TaskFlow.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Set the Application label to &lt;strong&gt;&lt;em&gt;TaskFlow&lt;/em&gt;&lt;/strong&gt;, and click Done.&lt;/li&gt;
  &lt;li&gt;Select the &lt;strong&gt;Assignments&lt;/strong&gt; tab
    &lt;ul&gt;
      &lt;li&gt;To assign it to a single user, select &lt;strong&gt;Assign &amp;gt; Assign to People&lt;/strong&gt; and choose your user&lt;/li&gt;
      &lt;li&gt;To assign it to a user group, select &lt;strong&gt;Assign &amp;gt; Assign to Groups&lt;/strong&gt; and choose your user group&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Done&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;update-the-audience-value-of-your-resource-apps-auth-server&quot;&gt;Update the audience value of your Resource app’s auth server&lt;/h2&gt;

&lt;p&gt;By default, Okta will issue an ID-JAG token for Agent0 with the audience (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt;) value set to that of the sample resource app (Todo0): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5001/&lt;/code&gt;. You must change this so the ID-JAG token includes an audience value that identifies your actual resource app’s authorization server.&lt;/p&gt;

&lt;p&gt;To do this, contact the Okta XAA team to replace your app’s audience value in Okta by sending an email to xaa@okta.com. Provide the following information to the Okta XAA team:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Okta Integrator Org URL:&lt;/em&gt;&lt;/strong&gt; ‘<span class="okta-preview-domain">https://{yourOktaDomain}</span>’&lt;br /&gt;
&lt;strong&gt;&lt;em&gt;Audience:&lt;/em&gt;&lt;/strong&gt; ‘http://yourresourceapps.authserver.org’
&lt;strong&gt;&lt;em&gt;Client ID from your own OAuth server:&lt;/em&gt;&lt;/strong&gt; [Agent0’s XAA client ID you created earlier]&lt;/p&gt;

&lt;p&gt;Please note that the Client ID you provide must be the client ID from your own OAuth server that was created earlier.&lt;/p&gt;

&lt;h1 id=&quot;establish-connections-between-agent0-and-your-resource-app&quot;&gt;Establish Connections between Agent0 and your resource app&lt;/h1&gt;

&lt;p&gt;Now that you have set up both requesting and resource apps, you need to establish that Agent0 can be trusted to make requests to your resource app.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Still in Admin Console, navigate to &lt;strong&gt;Applications &amp;gt; Applications &amp;gt; Agent0&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Go to the &lt;strong&gt;Manage Connections&lt;/strong&gt; tab&lt;/li&gt;
  &lt;li&gt;Under &lt;strong&gt;Apps providing consent&lt;/strong&gt;, select &lt;strong&gt;Add resource apps&lt;/strong&gt;, select &lt;strong&gt;TaskFlow&lt;/strong&gt;, then &lt;strong&gt;Save&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Confirm that your resource app appears under &lt;strong&gt;Apps providing consent&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now Agent0 and TaskFlow are connected.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-resource-app/image1-723faeb6e83b3230d953dadecfc1e00b0483aa4db06c25d6c1ca30a265f504ae.jpg&quot; alt=&quot; &quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;validate-that-your-resource-app-and-auth-server-work-as-intended&quot;&gt;Validate that your Resource App and Auth Server work as intended&lt;/h1&gt;

&lt;p&gt;Once the Okta XAA team confirms that your app’s audience value has been updated in Okta, Agent0 can make a Token Exchange request to Okta and will receive an ID-JAG with the correct audience.&lt;/p&gt;

&lt;p&gt;To test the end-to-end XAA flow with Agent0 to your authorization server, create a testing client that completes the following steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Agent0 signs the user in with OIDC.&lt;/li&gt;
  &lt;li&gt;Agent0 exchanges the ID token for an ID-JAG at Okta&lt;/li&gt;
  &lt;li&gt;Agent0 makes a token request with the ID-JAG at your authorization server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you need support with taking the steps above, contact xaa@okta.com.&lt;/p&gt;

&lt;p&gt;With testing complete, consider publicizing your resource app on the Okta Integration Network (OIN) catalog. Adding it to the catalog makes it easy for Okta’s roughly 18000 enterprise customers to learn about and add it to the suite of tools on their Okta dashboards.&lt;/p&gt;

&lt;h1 id=&quot;learn-more-about-cross-app-access-oauth-20-and-securing-your-applications&quot;&gt;Learn more about Cross App Access, OAuth 2.0, and securing your applications&lt;/h1&gt;

&lt;p&gt;If this walkthrough helped you understand more about how Cross App Access works in practice, consider learning more about&lt;/p&gt;

&lt;p&gt;📘 &lt;a href=&quot;https://xaa.dev/&quot;&gt;xaa.dev&lt;/a&gt; - a free, open sandbox that lets you explore Cross App Access end-to-end. No local setup. No infrastructure to provision. Just a working environment where you can see the protocol in action.&lt;br /&gt;
📘 &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/apps/apps-cross-app-access.htm&quot;&gt;Okta’s Cross App Access Documentation&lt;/a&gt; – official guides and admin docs to configure and manage Cross App Access in production&lt;br /&gt;
🎙️ &lt;a href=&quot;https://www.youtube.com/watch?v=qKs4k5Y1x_s&quot;&gt;Okta Developer Podcast on MCP and Cross App Access&lt;/a&gt; – hear the backstory, use cases, and why this matters for developers&lt;br /&gt;
📄 &lt;a href=&quot;https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/&quot;&gt;OAuth Identity Assertion Authorization Grant (IETF Draft)&lt;/a&gt; – the emerging standard that powers this flow&lt;/p&gt;

</description>
        <pubDate>Tue, 17 Feb 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/02/17/xaa-resource-app</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/02/17/xaa-resource-app</guid>
      </item>
    
      <item>
        <title>Make Secure App-to-App Connections Using Cross App Access</title>
        <description>&lt;p&gt;Imagine you built a note-taking app. It’s so successful that LargeCorp, an aptly named large enterprise corporation, signed on as a customer. To make it a power tool for your enterprise customers, you need to allow your app to integrate with other productivity tools, such as turning a note into a task in a to-do app.&lt;/p&gt;

&lt;p&gt;While common integration patterns work well for individual users, these patterns create security and compliance hurdles for large organizations.&lt;/p&gt;

&lt;h2 id=&quot;limitations-of-api-keys-and-oauth-in-enterprise-app-to-app-connectivity&quot;&gt;Limitations of API keys and OAuth in enterprise app-to-app connectivity&lt;/h2&gt;

&lt;p&gt;Connecting independent apps usually involves one of two common strategies. Both have significant drawbacks when used in a corporate environment:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;API keys and service accounts&lt;/strong&gt;
  These lack user context. They often lead to over-privileged access and create challenging rotation requirements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Standard OAuth 2.0&lt;/strong&gt;
  A much better, industry-standard best practice over API keys and service accounts, but this relies on individual user consent. IT admins cannot see or control which apps employees connect to, creating shadow IT risks and compliance and security concerns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;cross-app-access-xaa-extends-oauth-flows-to-manage-application-access&quot;&gt;Cross App Access (XAA) extends OAuth flows to manage application access&lt;/h2&gt;

&lt;p&gt;Cross App Access is an OAuth extension based on the &lt;a href=&quot;https://drafts.oauth.net/oauth-identity-assertion-authz-grant/draft-ietf-oauth-identity-assertion-authz-grant.html&quot;&gt;Identity Assertion Authorization Grant&lt;/a&gt;. It addresses these challenges by using the Enterprise Identity Provider (IdP) as a central broker and was proposed by a collaborative group of organizations and interested individuals.&lt;/p&gt;

&lt;p&gt;With XAA, the Identity Provider (IdP) facilitates a secure token exchange. This provides three main benefits.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;IT Governance - Admins centrally manage and approve app-to-app connections&lt;/li&gt;
  &lt;li&gt;Reduced friction - Users avoid repeated and confusing consent prompts&lt;/li&gt;
  &lt;li&gt;Granular security - Access is limited to specific users and specific tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read in depth about XAA 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; to better understand how this works and to look at the token exchange flow&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/2025/06/23/enterprise-ai&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Integrate Your Enterprise AI Tools with Cross-App Access&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Manage user and non-human identities, including AI in the enterprise with Cross App Access&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/semona-igama/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-semona-igama-03eb4c28aca3765f862b574e032d32f6f8186d04ae9f0db75bed9c74f48a9a3f.jpg&quot; alt=&quot;avatar-avatar-semona-igama.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/semona-igama/&quot;&gt;Semona Igama&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;p&gt;In this tutorial, we’ll add XAA to connect a note-taking app to a to-do app using &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt; as our testing ground.&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;#limitations-of-api-keys-and-oauth-in-enterprise-app-to-app-connectivity&quot; id=&quot;markdown-toc-limitations-of-api-keys-and-oauth-in-enterprise-app-to-app-connectivity&quot;&gt;Limitations of API keys and OAuth in enterprise app-to-app connectivity&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cross-app-access-xaa-extends-oauth-flows-to-manage-application-access&quot; id=&quot;markdown-toc-cross-app-access-xaa-extends-oauth-flows-to-manage-application-access&quot;&gt;Cross App Access (XAA) extends OAuth flows to manage application access&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#make-app-to-app-requests-using-cross-app-access&quot; id=&quot;markdown-toc-make-app-to-app-requests-using-cross-app-access&quot;&gt;Make app-to-app requests using Cross App Access&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#bring-your-own-requestor-app-to-the-xaadev-testing-site&quot; id=&quot;markdown-toc-bring-your-own-requestor-app-to-the-xaadev-testing-site&quot;&gt;Bring your own requestor app to the xaa.dev testing site&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#get-the-nestjs-project-with-oauth-and-openid-connect-oidc-started&quot; id=&quot;markdown-toc-get-the-nestjs-project-with-oauth-and-openid-connect-oidc-started&quot;&gt;Get the NestJS project with OAuth and OpenID Connect (OIDC) started&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#exchanging-an-id-token-for-an-access-token-for-another-app&quot; id=&quot;markdown-toc-exchanging-an-id-token-for-an-access-token-for-another-app&quot;&gt;Exchanging an ID token for an access token for another app&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#exchange-the-id-token-for-an-intermediary-id-jag-token-type&quot; id=&quot;markdown-toc-exchange-the-id-token-for-an-intermediary-id-jag-token-type&quot;&gt;Exchange the ID token for an intermediary ID-JAG token type&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#use-the-id-jag-token-to-request-an-access-token-for-a-separate-app&quot; id=&quot;markdown-toc-use-the-id-jag-token-to-request-an-access-token-for-a-separate-app&quot;&gt;Use the ID-JAG token to request an access token for a separate app&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#inspecting-the-xaa-token-exchange&quot; id=&quot;markdown-toc-inspecting-the-xaa-token-exchange&quot;&gt;Inspecting the XAA token exchange&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more-about-xaa-and-elevating-identity-security-using-oauth&quot; id=&quot;markdown-toc-learn-more-about-xaa-and-elevating-identity-security-using-oauth&quot;&gt;Learn more about XAA and elevating identity security using OAuth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;make-app-to-app-requests-using-cross-app-access&quot;&gt;Make app-to-app requests using Cross App Access&lt;/h2&gt;

&lt;p&gt;We’re using &lt;a href=&quot;https://nestjs.com/&quot;&gt;NestJS&lt;/a&gt; in this project. The tech stack relies on TypeScript, and we’ll use an OpenID Connect (OIDC) client library to communicate with the IdP and the to-do app’s OAuth Authorization server. Using a well-maintained OIDC client library is a best practice when creating apps that use OAuth flows, as it helps ensure you don’t make subtle errors in OAuth handshakes that compromise security.&lt;/p&gt;

&lt;p&gt;For this workshop, you need the following required tooling:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required tools&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; LTS version (v22 or higher at the time of this post)&lt;/li&gt;
  &lt;li&gt;Command-line terminal application&lt;/li&gt;
  &lt;li&gt;A code editor/Integrated development environment (IDE), such as &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; (VS Code)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&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;This code project is best for developers with web development and TypeScript experience and familiarity with OAuth and OpenID Connect (OIDC) flows at a high level.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to skip directly to the working project, you can find it &lt;a href=&quot;https://github.com/oktadev/okta-js-xaa-requestor-example&quot;&gt;in the GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bring-your-own-requestor-app-to-the-xaadev-testing-site&quot;&gt;Bring your own requestor app to the xaa.dev testing site&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt; testing site supports testing local client apps. It’s IdP-agnostic, meaning it’s focused on the spec and education, not on a specific company’s product line. In this scenario, we can verify whether our client app, the note-taking app, handles the token exchange with an IdP and the resource app’s authorization server. The best part about this testing site is that it’s self-contained and works out of the box. So you don’t need to create an account with an IdP, nor do you have a resource app with a conformant OAuth authorization server! We just have to bring our client code for testing! Yay for simplicity!&lt;/p&gt;

&lt;p&gt;You can read more about the site here:&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/2026/01/20/xaa-dev-playground&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Introducing xaa.dev: A Playground for Cross App Access&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Explore Cross App Access end-to-end with xaa.dev, a free, open playground that lets you test the XAA protocol without any local setup or infrastructure.&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/sohail-pathan/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-sohail-pathan-fa148e78133752dcc86034268bffe3367e2708874b1ea957b09712e8937b8cc7.jpg&quot; alt=&quot;avatar-avatar-sohail-pathan.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/sohail-pathan/&quot;&gt;Sohail Pathan&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;p&gt;Let’s register our note-taking app now.&lt;/p&gt;

&lt;p&gt;In your browser, navigate to &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt;. The main site provides information about the players in this flow, and you can test the XAA flow step by step there. Please take a moment to step through the flow to get a better sense of the code we’ll build.&lt;/p&gt;

&lt;p&gt;When you’re ready, navigate to &lt;strong&gt;Developer&lt;/strong&gt; &amp;gt; &lt;strong&gt;Register Client&lt;/strong&gt;. Add a totally made-up email for more fun when registering.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;+ Register New Client&lt;/strong&gt; and fill out the required information:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Application Name&lt;/strong&gt; - I used “Notes App”&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Redirect URIs&lt;/strong&gt; - Enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:3000/auth/callback&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Post-Logout Redirect URIs&lt;/strong&gt; - Enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:3000&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Resource Connections &amp;gt; Add Resource&lt;/strong&gt; - Choose “Todo0 Resource App” and mark “todos.read” as your allowed scopes before clicking the Add Connection button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once all necessary fields have been filled select &lt;strong&gt;Register App&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You’ll see a modal with the Client ID and Client Secret. The xaa.dev testing site also provides credentials for the resource app’s authorization server - the Resource Client ID and Resource Client Secret. Copy all four values. We need to add these to our project.&lt;/p&gt;

&lt;h2 id=&quot;get-the-nestjs-project-with-oauth-and-openid-connect-oidc-started&quot;&gt;Get the NestJS project with OAuth and OpenID Connect (OIDC) started&lt;/h2&gt;

&lt;p&gt;You’ll use a starter note-taking app project written in NestJS. Before you get too excited, remember this is a demo app. While the note-taking features are minimal, it does include built-in authentication.&lt;/p&gt;

&lt;p&gt;Open a terminal window and run the following commands to get a local copy of the project in a directory named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-xaa-project&lt;/code&gt; and install dependencies. Feel free to fork the repo to 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-js-xaa-requestor-example.git okta-xaa-project
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;okta-xaa-project
npm ci
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the project in your IDE. Let’s go over the main components and framework choices so you don’t have to discover everything on your own:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The NestJS project depends on &lt;a href=&quot;https://expressjs.com/&quot;&gt;Express&lt;/a&gt; as the base engine and uses &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Views for the landing page and the notes interface use &lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot;&gt;Nunjucks&lt;/a&gt; as the templating engine.&lt;/li&gt;
  &lt;li&gt;Relies on the &lt;a href=&quot;https://github.com/panva/openid-client/tree/main&quot;&gt;openid-client&lt;/a&gt; to handle all OAuth handshakes. It’s an OIDC client library for JavaScript runtimes.&lt;/li&gt;
  &lt;li&gt;There’s a basic interceptor implementation that logs HTTP requests and responses to the console. This way, we can see the token exchange flow.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The app requires a client ID, client secret, resource client ID, and resource client secret to run. Let’s add those to the project.&lt;/p&gt;

&lt;p&gt;Rename the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env.example&lt;/code&gt; file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt;. It already has variables defined and values added to match the URI of the XAA testing site components. Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLIENT_ID&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CLIENT_SECRET&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_CLIENT_ID&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_CLIENT_SECRET&lt;/code&gt; values with the values from the XAA testing site.&lt;/p&gt;

&lt;p&gt;The app should now run, but it still won’t make a successful cross-app access request. Serve the app using the command shown:&lt;/p&gt;

&lt;div class=&quot;language-sh 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 to &lt;a href=&quot;http://localhost:3000&quot;&gt;http://localhost:3000&lt;/a&gt;. You should see a landing page that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-client/notes-app-fab860469237f856ce30c43a5701eb5b3d3d2d578cbfa873bcc4c1879315153c.jpg&quot; alt=&quot;The notes app landing page with a log in button in the top header&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Feel free to sign in. You’re redirected to the XAA testing site’s IdP for the user challenge. Enter the email address and any combination of numbers for the one-time password. You’ll redirect to the notes view and see something like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-client/notes-start-ed0d8d62e286046a35c7dc90aac2f6eb1a9b2dea0416bc99185471b0c0dc80c7.jpg&quot; alt=&quot;The notes app after signing in. The left nav has notes, the middle section displays the selected note, and the right side shows an empty todo pane&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are no todos yet, and in the IDE’s console we see logging and errors. Each request and response to the XAA testing site’s components has a corresponding log entry. We see the IdP’s redirect with the authorization code, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; to get tokens along with the request params, and a request to the todo API, which returns a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;401 Unauthorized&lt;/code&gt; HTTP status code. We need to add the code for the XAA token exchange. Stop serving the app by entering &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt; in the terminal.&lt;/p&gt;

&lt;h2 id=&quot;exchanging-an-id-token-for-an-access-token-for-another-app&quot;&gt;Exchanging an ID token for an access token for another app&lt;/h2&gt;

&lt;p&gt;When you sign in to the note-taking app, the IdP issues an ID token. From here, the XAA token flow is a two-step process:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The note-taking app requests the IDP’s OAuth authorization server to exchange the ID token for a trustworthy intermediary token type, an Identity Assertion JSON Web Token (JWT) also known as ID-JAG, that the todo app recognizes and supports.&lt;/li&gt;
  &lt;li&gt;The todo app’s OAuth authorization server exchanges the intermediary token and issues an access token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the access token in hand, the note-taking app can make resource requests to the todo app’s resource server.&lt;/p&gt;

&lt;p&gt;First, we request the trustworthy intermediary token type, the ID-JAG token.&lt;/p&gt;

&lt;h3 id=&quot;exchange-the-id-token-for-an-intermediary-id-jag-token-type&quot;&gt;Exchange the ID token for an intermediary ID-JAG token type&lt;/h3&gt;

&lt;p&gt;In the IDE, open the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/auth/auth.service.ts&lt;/code&gt; file. This file contains code for authentication and the OAuth exchange, along with some utility functions. You already have the code to sign in and have the ID token. We’ll continue using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openid-client&lt;/code&gt; library for the XAA token exchanges. Find the private helper method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exchangeIdTokenForIdJag()&lt;/code&gt;. The body of the method has a comment:&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;c1&quot;&gt;// add logic to return an ID-JAG token given the user's ID token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We need to replace the inner workings of this method to return the ID-JAG token instead of an empty promise. No empty promises for us! Our promises are as good as tokens. 👻&lt;/p&gt;

&lt;p&gt;Replace the code within the method as shown, then I’ll walk through each code block.&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;cm&quot;&gt;/**
 * Exchange ID token for ID-JAG token (step 1 of ID-JAG flow)
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;exchangeIdTokenForIdJag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resourceUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;nb&quot;&gt;Promise&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tokenExchangeParams&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;requested_token_type&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;urn:ietf:params:oauth:token-type:id-jag&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;audience&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;subject_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;subject_token_type&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;urn:ietf:params:oauth:token-type:id_token&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;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&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; &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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tokenExchangeResponse&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;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;genericGrantRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;config&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;urn:ietf:params:oauth:grant-type:token-exchange&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;tokenExchangeParams&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tokenExchangeResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;access_token&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;In this first exchange, we call the IdP. The IdP acts as the broker between the two apps as it’s the trusted source.&lt;/p&gt;

&lt;p&gt;Let’s step through the key parts of the first code block where we set the token exchange parameters:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requested_token_type&lt;/code&gt;&lt;/strong&gt; - we’re asking the IDP for the ID-JAG token&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;audience&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resource&lt;/code&gt;&lt;/strong&gt; - the authorization server and the todo API we’re requesting resources from&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subject_token&lt;/code&gt;&lt;/strong&gt; - the token we’re using for this exchange&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subject_token_type&lt;/code&gt;&lt;/strong&gt; - the type of the token we’re using for the exchange&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scopes&lt;/code&gt;&lt;/strong&gt; - the requested scopes, such as reading todos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once we have all these parameters set, we can call the IdP. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openid-client&lt;/code&gt; library has a function for making generic grant requests. We can use it to request the token exchange grant type. While the return value is not an access token, the grant request relies on existing OAuth models that defined the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; response parameter.&lt;/p&gt;

&lt;p&gt;Let’s call the method so we can test it out. Find the comment:&lt;/p&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;// Step 1: Exchange ID token for ID-JAG token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exchangeIdTokenForAccessToken()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Add the call to the method 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;c1&quot;&gt;// Step 1: Exchange ID token for ID-JAG token&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idJagToken&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exchangeIdTokenForIdJag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;idpConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;idToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resourceUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;scope&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;We’re adding configuration information, including the IdP, client ID, and client secret. And we have some other required configuration values pulled from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt; file, such as the servers for the todo app and the scopes.&lt;/p&gt;

&lt;p&gt;We’ll get the signed Identity Assertion JWT Authorization grant when the call succeeds. This is a signed token from the IdP, so whenever we exchange it in the next step, the recipient knows it’s trustworthy. Step one complete. ✅&lt;/p&gt;

&lt;p&gt;Feel free to start the app and check the console log for your first exchange request. You should see the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LOG [OAuth HTTP] → POST idp.xaa.dev/token&lt;/code&gt; in the console. Below that, you’ll see the token exchange parameters that look something like this:&lt;/p&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;DEBUG [OAuth HTTP]   body:
    requested_token_type=urn:ietf:params:oauth:token-type:id-jag
    audience=https://auth.resource.xaa.dev
    resource=https://api.resource.xaa.dev
    subject_token=eyJhbGc...IdoRppJyZmV9Q
    subject_token_type=urn:ietf:params:oauth:token-type:id_token
    scope=todos.read
    grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The call to get todos will still fail, but you can see the first exchange request in action! 🚀&lt;/p&gt;

&lt;h3 id=&quot;use-the-id-jag-token-to-request-an-access-token-for-a-separate-app&quot;&gt;Use the ID-JAG token to request an access token for a separate app&lt;/h3&gt;

&lt;p&gt;With the ID-JAG token in hand, we can now move on to the second exchange, exchanging the ID-JAG intermediary token for an access token to the todo app. We make this exchange with the todo app’s OAuth authorization server. The IdP oversees both the note-taking app and the todo app, and trust domains between the two apps facilitate this flow. Remember, in our first exchange, we had to specify the audience for the ID-JAG token in our request - the todo app.&lt;/p&gt;

&lt;p&gt;Back in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src/auth/auth.service.ts&lt;/code&gt;, find the comment:&lt;/p&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;// add logic to return an access token given the ID-JAG token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This comment is in the placeholder code for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exchangeIdJagForAccessToken()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Replace the placeholder code to make the exchange. Your code will look 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;cm&quot;&gt;/**
  * Exchange ID-JAG token for access token (step 2 of ID-JAG flow)
  */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;exchangeIdJagForAccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;idJagToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;nb&quot;&gt;Promise&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;jwtBearerParams&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;assertion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idJagToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;join&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; &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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTokenResponse&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;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;genericGrantRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;config&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;urn:ietf:params:oauth:grant-type:jwt-bearer&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;jwtBearerParams&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTokenResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;access_token&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;We’re following a similar pattern to the first exchange, with a difference in the grant request. This time, the parameters include an assertion, the ID-JAG token. And we make the grant request to the todo app’s OAuth authorization server with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;urn:ietf:params:oauth:grant-type:jwt-bearer&lt;/code&gt; grant type. This exchange relies upon a pre-existing spec where one can use a bearer JWT for as a grant type to request an access token. That’s what we’re doing in this step.&lt;/p&gt;

&lt;p&gt;Next, we’ll call this method in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exchangeIdTokenForAccessToken()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Find the comment:&lt;/p&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;// Step 2: Exchange ID-JAG token for access token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Because we’re calling a new authorization server, the todo app’s OAuth authorization server, we first need to read the well-known discovery docs. The discovery docs include information about the authorization server, such as the server’s capabilities and endpoints, including the token endpoint. Since we’re authenticating with the todo app’s authorization server, not the IdP, we use the resource app’s credentials here. The todo app’s authorization server recognizes RESOURCE_CLIENT_ID and RESOURCE_CLIENT_SECRET, not your notes app’s credentials. We’ve been using a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetch&lt;/code&gt; implementation to capture the logging you see, so we must include that implementation in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openid-client&lt;/code&gt; too. Then make the call to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exchangeIdJagForAccessToken()&lt;/code&gt; helper method. Your code will look 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;c1&quot;&gt;// Step 2: Exchange ID-JAG token for access token&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceAuthConfig&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;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;discovery&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;URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;authServerUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resourceClientId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resourceClientSecret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ClientSecretPost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;resourceClientSecret&lt;/span&gt; &lt;span class=&quot;o&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;span class=&quot;nx&quot;&gt;resourceAuthConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;openidClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;customFetch&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;loggedFetch&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;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exchangeIdJagForAccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;resourceAuthConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;idJagToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;scope&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;Make sure to remove any placeholder implementation. Step two complete. ✅&lt;/p&gt;

&lt;p&gt;The code to make a request to the todo API using the bearer token already exists in the project. Let’s try running the app now using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;inspecting-the-xaa-token-exchange&quot;&gt;Inspecting the XAA token exchange&lt;/h2&gt;

&lt;p&gt;After you authenticate, you’ll see the notes and the todos! 🎉&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-client/notes-todos-631e2643399d8e41702e2d3460371f7d6e112fc1c0f03a7b097aec8e5217e649.jpg&quot; alt=&quot;The notes app with todos listed on the side&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the terminal console, you’ll see each step of the handshake and requests:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Authentication in the notes app with the IdP returning the ID token&lt;/li&gt;
  &lt;li&gt;Exchanging the ID token for an ID-JAG token with the IDP’s OAuth authorization server&lt;/li&gt;
  &lt;li&gt;Exchanging the ID-JAG token for an access token with the todo app’s OAuth authorization server&lt;/li&gt;
  &lt;li&gt;Call the todo app’s resource server (the API)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Feel free to inspect each step of this flow, the request parameters, and the responses.&lt;/p&gt;

&lt;p&gt;These steps allow an app to make requests to a third-party app within enterprise systems securely. You can find the completed project &lt;a href=&quot;https://github.com/oktadev/okta-js-xaa-requestor-example&quot;&gt;in the GitHub repo&lt;/a&gt; with instructions to also test on &lt;a href=&quot;https://github.com/features/codespaces&quot;&gt;GitHub Codespaces&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;learn-more-about-xaa-and-elevating-identity-security-using-oauth&quot;&gt;Learn more about XAA and elevating identity security using OAuth&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this post on making secure cross-app requests for enterprise use cases. If you found this post interesting, I encourage you to check out these links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2025/09/03/cross-app-access&quot;&gt;Build Secure Agent-to-App Connections with Cross App Access (XAA)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://drafts.oauth.net/oauth-identity-assertion-authz-grant/draft-ietf-oauth-identity-assertion-authz-grant.html&quot;&gt;Identity Assertion JWT Authorization Grant&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2024/04/30/express-universal-logout&quot;&gt;How to Instantly Sign a User Out across All Your Apps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2024/02/29/net-scim&quot;&gt;How to Manage User Lifecycle with .NET and SCIM&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2023/09/25/oauth-api-tokens&quot;&gt;Why You Should Migrate to OAuth 2.0 From Static API Tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to follow us on &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/OktaDev/&quot;&gt;YouTube 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, 10 Feb 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/02/10/xaa-client</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/02/10/xaa-client</guid>
      </item>
    
      <item>
        <title>Take User Provisioning to the Next Level with Entitlements</title>
        <description>&lt;p&gt;When you work on B2B SaaS apps used by large customer organizations, synchronizing those customers’ users within your software system is tricky! You must synchronize user profile information and the user attributes required for access control management. Customers with large workforces may have thousands of users to manage. They demand a speedy onboarding process, including automated user provisioning from their identity provider!&lt;/p&gt;

&lt;p&gt;Managing users across domains is critical to making B2B apps enterprise-scalable. In the &lt;a href=&quot;/blog/tags/enterprise-ready-workshops/&quot;&gt;Enterprise-Ready and Enterprise-Maturity on-demand workshop series&lt;/a&gt;, we tackle the dilemmas faced by developers of SaaS products wanting to scale their apps to enterprise customers. We iterate on a fictitious B2B Todo app more secure and capable for enterprise customers using industry-recognized standards such as OpenID Connect (OIDC) authentication and System for Cross-Domain Identity Management (SCIM) for user provisioning. In this workshop, you build upon a previous workshop introducing automated user provisioning to add support for users’ access management and permissions attributes—their entitlements.&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;Note&lt;/strong&gt; &lt;br /&gt;
    This post requires Okta Identity Governance (OIG) features in your Okta org. &lt;a href=&quot;https://developer.okta.com/signup/&quot;&gt;Sign up for a new Integrator Free plan&lt;/a&gt; to continue.
    &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Posts in the on-demand workshop series&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1. &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;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2. &lt;a href=&quot;/blog/2023/07/28/oidc_workshop&quot;&gt;Enterprise-Ready Workshop: Authenticate with OpenID Connect&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3. &lt;a href=&quot;/blog/2023/07/28/scim-workshop&quot;&gt;Enterprise-Ready Workshop: Manage Users with SCIM&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4. &lt;a href=&quot;/blog/2023/07/28/terraform-workshop&quot;&gt;Enterprise Maturity Workshop: Terraform&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5. &lt;a href=&quot;/blog/2023/09/15/workflows-workshop&quot;&gt;Enterprise Maturity Workshop: Automate with no-code Okta Workflows&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6. &lt;a href=&quot;/blog/2024/04/30/express-universal-logout&quot;&gt;How to Instantly Sign a User Out across All Your Apps&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7. &lt;strong&gt;Take User Provisioning to the Next Level with Entitlements&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This workshop walks you through adding the code to support entitlements in a sample application with three broad sections:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Introduction to the base application, tools, and the development process&lt;/li&gt;
  &lt;li&gt;See your application’s user and entitlements information in Okta&lt;/li&gt;
  &lt;li&gt;Use Okta to manage user roles and custom entitlements&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to skip to the completed code project for this workshop, you can find it in the &lt;a href=&quot;https://github.com/oktadev/okta-enterprise-ready-workshops/tree/entitlements-workshop-complete&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements-completed&lt;/code&gt; branch on the GitHub repo&lt;/a&gt;.&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;#manage-users-at-scale-using-system-for-cross-domain-identity-management-scim&quot; id=&quot;markdown-toc-manage-users-at-scale-using-system-for-cross-domain-identity-management-scim&quot;&gt;Manage users at scale using System for Cross-domain Identity Management (SCIM)&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#prepare-the-expressjs-api-project&quot; id=&quot;markdown-toc-prepare-the-expressjs-api-project&quot;&gt;Prepare the Express.js API project&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#serve-the-expressjs-api-and-test-the-roles-scim-endpoint&quot; id=&quot;markdown-toc-serve-the-expressjs-api-and-test-the-roles-scim-endpoint&quot;&gt;Serve the Express.js API and test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; SCIM endpoint&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#support-user-roles-in-the-database&quot; id=&quot;markdown-toc-support-user-roles-in-the-database&quot;&gt;Support user roles in the database&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#connect-okta-to-the-scim-server&quot; id=&quot;markdown-toc-connect-okta-to-the-scim-server&quot;&gt;Connect Okta to the SCIM server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#create-an-okta-scim-application-for-entitlements-governance&quot; id=&quot;markdown-toc-create-an-okta-scim-application-for-entitlements-governance&quot;&gt;Create an Okta SCIM application for entitlements governance&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#scim-schemas-and-resources&quot; id=&quot;markdown-toc-scim-schemas-and-resources&quot;&gt;SCIM schemas and resources&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#harness-typescript-to-conform-to-scim-schemas&quot; id=&quot;markdown-toc-harness-typescript-to-conform-to-scim-schemas&quot;&gt;Harness TypeScript to conform to SCIM schemas&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#scim-list-response&quot; id=&quot;markdown-toc-scim-list-response&quot;&gt;SCIM list response&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#return-database-defined-roles-in-the-scim-roles-endpoint&quot; id=&quot;markdown-toc-return-database-defined-roles-in-the-scim-roles-endpoint&quot;&gt;Return database-defined roles in the SCIM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#scim-resource-types&quot; id=&quot;markdown-toc-scim-resource-types&quot;&gt;SCIM resource types&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#add-roles-to-the-scim-users-endpoints&quot; id=&quot;markdown-toc-add-roles-to-the-scim-users-endpoints&quot;&gt;Add roles to the SCIM Users endpoints&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#update-the-scim-add-users-call-to-include-roles&quot; id=&quot;markdown-toc-update-the-scim-add-users-call-to-include-roles&quot;&gt;Update the SCIM add users call to include roles&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#add-roles-when-getting-a-list-of-users-in-scim&quot; id=&quot;markdown-toc-add-roles-when-getting-a-list-of-users-in-scim&quot;&gt;Add roles when getting a list of users in SCIM&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#update-the-users-call-so-scim-clients-can-set-their-roles&quot; id=&quot;markdown-toc-update-the-users-call-so-scim-clients-can-set-their-roles&quot;&gt;Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users&lt;/code&gt; call so SCIM clients can set their roles&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#entitlements-discovery-in-okta&quot; id=&quot;markdown-toc-entitlements-discovery-in-okta&quot;&gt;Entitlements discovery in Okta&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#syncing-user-entitlements&quot; id=&quot;markdown-toc-syncing-user-entitlements&quot;&gt;Syncing user entitlements&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#schema-discovery-for-custom-entitlements&quot; id=&quot;markdown-toc-schema-discovery-for-custom-entitlements&quot;&gt;Schema discovery for custom entitlements&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#multi-tenant-use-cases-for-entitlements&quot; id=&quot;markdown-toc-multi-tenant-use-cases-for-entitlements&quot;&gt;Multi-tenant use cases for entitlements&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#use-scim-to-manage-user-provisioning-and-entitlements&quot; id=&quot;markdown-toc-use-scim-to-manage-user-provisioning-and-entitlements&quot;&gt;Use SCIM to manage user provisioning and entitlements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;manage-users-at-scale-using-system-for-cross-domain-identity-management-scim&quot;&gt;Manage users at scale using System for Cross-domain Identity Management (SCIM)&lt;/h2&gt;

&lt;p&gt;The Todo app tech stack uses a React frontend and an Express API backend. For this workshop, you need the following required tooling:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required tools&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nodejs.org/en&quot;&gt;Node.js&lt;/a&gt; v18 or higher&lt;/li&gt;
  &lt;li&gt;Command-line terminal application&lt;/li&gt;
  &lt;li&gt;A code editor/Integrated development environment (IDE), such as &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; (VS Code)&lt;/li&gt;
  &lt;li&gt;An HTTP client testing tool, such as &lt;a href=&quot;https://www.postman.com/&quot;&gt;Postman&lt;/a&gt; or the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=mkloubert.vscode-http-client&quot;&gt;HTTP Client&lt;/a&gt; VS Code extension&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;VS Code has integrated terminals and HTTP client extensions that allow you to work out of this one application for almost everything required in this workshop. The IDE also supports TypeScript, so you’ll get quicker responses on type errors and help with importing modules.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Follow the instructions in the getting started guide for installing the required tools and serving the Todo application.&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/2023/07/27/enterprise-ready-getting-started&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;How to Get Going with the On-Demand SaaS Apps Workshops&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Start your journey to identity maturity for your SaaS applications in the enterprise-ready workshops! This post covers installing and running the base application in preparation for the upcoming workshops.&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;p&gt;You’ll build upon a prior workshop introducing syncing users across systems using the &lt;a href=&quot;https://scim.cloud/&quot;&gt;System for Cross-domain Identity Management&lt;/a&gt; (SCIM) protocol.&lt;/p&gt;

&lt;p&gt;In this workshop, you’ll dive deeper into automated user provisioning by adding the user attributes required for access management, such as user roles, licensing, permissions, or something else you use to denote what actions a user has access to. The access management attributes of users are known by the generic term, user entitlements. Then, we will continue diving deeper into supporting customized user entitlements using the SCIM protocol.&lt;/p&gt;

&lt;p&gt;Before we get going with user entitlements, you’ll first step through the interactive and fun &lt;a href=&quot;/blog/2023/07/28/scim-workshop&quot;&gt;Enterprise-Ready Workshop: Manage Users with SCIM&lt;/a&gt; workshop to get the SCIM overview, set up the code and your Okta account, and see how the protocol works. I’ll settle down with a cup of tea and a good book and wait while you learn about SCIM and are ready to continue! 🫖🍵📚&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/2023/07/28/scim-workshop&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Enterprise-Ready Workshop: Manage users with SCIM&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;In this workshop, you will add SCIM support to a sample application, so that user changes made in your app can sync to your customer's Identity Provider!&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/semona-igama/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-semona-igama-03eb4c28aca3765f862b574e032d32f6f8186d04ae9f0db75bed9c74f48a9a3f.jpg&quot; alt=&quot;avatar-avatar-semona-igama.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/semona-igama/&quot;&gt;Semona Igama&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;h3 id=&quot;prepare-the-expressjs-api-project&quot;&gt;Prepare the Express.js API project&lt;/h3&gt;

&lt;p&gt;Start from a clean code project by using the SCIM workshop’s completed project code from the &lt;a href=&quot;https://github.com/oktadev/okta-enterprise-ready-workshops/tree/scim-workshop-complete&quot;&gt;scim-workshop-complete&lt;/a&gt; branch. I’ll post the instructions using &lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt;, but you can download the code as &lt;a href=&quot;https://github.com/oktadev/okta-enterprise-ready-workshops/archive/refs/heads/scim-workshop-complete.zip&quot;&gt;a zip file&lt;/a&gt; if you prefer and skip the Git command.&lt;/p&gt;

&lt;p&gt;Get a local copy of the completed SCIM workshop code and install dependencies by running the following commands in your terminal:&lt;/p&gt;

&lt;div class=&quot;language-sh 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; scim-workshop-complete https://github.com/oktadev/okta-enterprise-ready-workshops.git
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;okta-enterprise-ready-workshops
npm ci
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open the code project in your IDE. We’ll work exclusively within the Express.js API for this project, and the code files for the API are in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements.ts&lt;/code&gt;. We’ll define the API routes for user entitlements in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Let’s start by hard-coding an API endpoint for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; that returns a list of roles. In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements.ts&lt;/code&gt; file, add the following code:&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;Router&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;express&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;rolesRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;rolesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Todo-er&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;Admin&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;roles&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;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim.ts&lt;/code&gt;. We need to register the endpoint in the Express app by including it as part of the SCIM routes.&lt;/p&gt;

&lt;p&gt;At the top of the file, import &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rolesRoutes&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;rolesRoute&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;./entitlements&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;At the bottom of the file below the existing code, add&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;scimRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&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;/Roles&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;rolesRoute&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;to register the endpoint. Let’s make sure everything works!&lt;/p&gt;

&lt;h3 id=&quot;serve-the-expressjs-api-and-test-the-roles-scim-endpoint&quot;&gt;Serve the Express.js API and test the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; SCIM endpoint&lt;/h3&gt;

&lt;p&gt;In the terminal, start the API by running&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm run serve-api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command serves the API on port 3333. Launch your HTTP client and call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint:&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;http://localhost:3333/scim/v2/Roles&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do you see a successful response with a list of roles?&lt;/p&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;HTTP/1.1 200 OK

[
  &quot;Todo-er&quot;,
  &quot;Admin&quot;
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Take a look at the terminal output. You’ll see output recording the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt; request!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/user-entitlements-workshop/morgan-968d824c03d48283182cce88f47f8163b3a9af02a9b98ed786a98e1a24589296.jpg&quot; alt=&quot;Terminal output showing the GET request to the /Roles route and a 200OK HTTP response&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The project uses &lt;a href=&quot;https://github.com/expressjs/morgan&quot;&gt;Morgan&lt;/a&gt;, a library that automatically adds HTTP logging to the Express API. The terminal output includes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; request payloads, so it’s an excellent way to track the SCIM calls as you work through the workshop.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run serve-api&lt;/code&gt; process watches for changes and automatically updates the API, so we don’t need to stop and restart it constantly. But we’re about to make some significant changes. Stop serving the API by entering &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;c&lt;/kbd&gt; in the terminal so we can prepare the database.&lt;/p&gt;

&lt;h2 id=&quot;support-user-roles-in-the-database&quot;&gt;Support user roles in the database&lt;/h2&gt;

&lt;p&gt;The Todo app database needs to support roles; we’ve hardcoded roles so far. It’s time to bring the database to the party. A fancier SaaS app might allow each customer to define their roles. We’ll skip that level of customizability for now and focus on the simplest case. For this workshop, we’ll define supported roles for all Todo app customers instead of allowing role configurations per organization. Taking the position of application roles instead of organization roles makes our database modeling easier. I’ll discuss ways to add per-organization configurability later in the post.&lt;/p&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/prisma/schema.prisma&lt;/code&gt;. Add the role model at the end of the file.&lt;/p&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;model Role {
  id Int @id @default(autoincrement())
  name String
  users User[]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A user may have zero or more roles. Update the user model to add roles so that the user model looks like this:&lt;/p&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;model User {
  id         Int    @id @default(autoincrement())
  email      String
  password   String?
  name       String
  Todo       Todo[]
  org        Org?    @relation(fields: [orgId], references: [id])
  orgId      Int?
  externalId String?
  active     Boolean?
  roles      Role[]
  @@unique([orgId, externalId])
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the roles model defined, it’s time to update the database to match the model. We’ll start with a fresh, clean database for this project. In the terminal run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx prisma migrate reset &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It helps to have some seed data so we can get going. Here, we’ll define roles available within the Todo app. A user can be a “Todo-er,” “Todo Auditor,” and “Manager.” Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/prisma/seed_script.ts&lt;/code&gt; and replace the entire file with the code below:&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;PrismaClient&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;@prisma/client&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PrismaClient&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;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;main&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;org&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;prisma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data&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;domain&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;gridco.example&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;apikey&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;123123&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;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&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;Created org Portal&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;org&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Roles defined by the Todo app&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;na&quot;&gt;name&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;Todo-er&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;na&quot;&gt;name&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;Todo Auditor&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;na&quot;&gt;name&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;Manager&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createdRoles&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;nb&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&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;for&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;role&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createdRoles&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;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&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;Created role &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;role&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;somnusUser&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;prisma&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;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data&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;name&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;Somnus Henderson&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;email&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;somnus.henderson@gridco.example&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;password&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;correct horse battery staple&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;orgId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;org&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;externalId&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;31&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;active&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;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&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;Created user Somnus&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;somnusUser&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;trinityUser&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;prisma&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;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;data&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;name&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;Trinity JustTrinity&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;email&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;trinity@gridco.example&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;password&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;Zion&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;orgId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;org&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;externalId&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;32&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;active&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;roles&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;connect&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;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;createdRoles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Todo-er&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;id&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;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&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;Created user Trinity&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;trinityUser&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;main&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;then&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;p&quot;&gt;()&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$disconnect&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;catch&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;e&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;nx&quot;&gt;prisma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;$disconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;exit&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;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Save the file and run the npm script in the terminal to seed the database.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm run init-db
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll see console output for each newly created database record. 🎉&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspect the database records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can inspect the database records using &lt;a href=&quot;https://www.prisma.io/studio&quot;&gt;Prisma Studio&lt;/a&gt;. In a separate terminal, run&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx prisma studio
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which launches a web interface to view the database. The site URL is usually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5555&lt;/code&gt;, shown in the terminal output. Open the site in your browser to view the database tables, records, and relationships.&lt;/p&gt;

&lt;h2 id=&quot;connect-okta-to-the-scim-server&quot;&gt;Connect Okta to the SCIM server&lt;/h2&gt;

&lt;p&gt;The SCIM Client (the identity provider, Okta) makes requests upon objects held by the SCIM Server (the Todo app).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/scim-workshop/scim-diagram-4cfb2cb57d2031d826a9221b3fdf59284e2df35657a79c53118f3bb776be0440.jpg&quot; alt=&quot;SCIM workflow showing the Identity Provider requests the SCIM server with GET, POST, PUT, and DEL user calls and the SCIM server responds with a standard SCIM interface&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;First, we need to serve the API so Okta can access it. You’ll use a temporary tunnel for local development that makes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost:3333&lt;/code&gt; publicly accessible so that Okta, the SCIM client, can call your API, the SCIM server. I’ll include the instructions using an NPM library that we don’t have to install or sign up for, but feel free to use your favorite tunneling system if you have one.&lt;/p&gt;

&lt;p&gt;You need two terminal sessions.&lt;/p&gt;

&lt;p&gt;In one terminal, serve the API using the command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm run serve-api
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the second terminal, you’ll run the local tunnel. Run the command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx localtunnel &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 3333
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This creates a tunnel for the application serving on port 3333. The console output displays the tunnel URL in the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://{yourTunnelSubdomain}.loca.lt&lt;/code&gt;, such as:&lt;/p&gt;

&lt;div class=&quot;language-console 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;go&quot;&gt;your URL is: https://awesome-devs-club.loca.lt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll need this tunnel URL to configure the Okta application.&lt;/p&gt;

&lt;h2 id=&quot;create-an-okta-scim-application-for-entitlements-governance&quot;&gt;Create an Okta SCIM application for entitlements governance&lt;/h2&gt;

&lt;p&gt;In the prerequisite SCIM workshop, you added a SCIM application in Okta to connect to the Todo app. We must do something similar to connect SCIM with entitlements support.&lt;/p&gt;

&lt;p&gt;Sign into your &lt;a href=&quot;https://developer.okta.com/login/&quot;&gt;Okta Integrator Free account&lt;/a&gt;. In the Admin Console, navigate to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt;. Press the &lt;strong&gt;Browse App Catalog&lt;/strong&gt; button to create a new Okta SCIM application.&lt;/p&gt;

&lt;p&gt;In the search bar, search for “(Header Auth) Governance with SCIM 2.0” and select the app. Press &lt;strong&gt;Add Integration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You’ll see a configuration view with two tabs. Press &lt;strong&gt;Next&lt;/strong&gt; on the &lt;strong&gt;General settings&lt;/strong&gt; tab. Leave default settings on the &lt;strong&gt;Sign-On Options&lt;/strong&gt; tab and press &lt;strong&gt;Done&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You’ll navigate to your newly created Okta application to add specific configurations about the Todo app.&lt;/p&gt;

&lt;p&gt;First, you need to enable Identity Governance. Navigate to the &lt;strong&gt;General&lt;/strong&gt; tab and find the &lt;strong&gt;Identity Governance&lt;/strong&gt; section. Press &lt;strong&gt;Edit&lt;/strong&gt; to select &lt;strong&gt;Enabled&lt;/strong&gt; for &lt;strong&gt;Governance Engine&lt;/strong&gt;. Remember to &lt;strong&gt;Save&lt;/strong&gt; your change.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Provisioning&lt;/strong&gt; tab and press the &lt;strong&gt;Configure API Integration&lt;/strong&gt; button. Check the &lt;strong&gt;Enable API integration&lt;/strong&gt; checkbox—two more form fields display.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;In &lt;strong&gt;Base URL&lt;/strong&gt; field, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://{yourTunnelSubdomain}.loca.lt/scim/v2&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;It will look like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://awesome-devs-club.loca.lt/scim/v2&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the &lt;strong&gt;API Token&lt;/strong&gt; field, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bearer 123123&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;press &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Provisioning&lt;/strong&gt; tab has more options to configure within the &lt;strong&gt;Settings&lt;/strong&gt; side nav.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;To App&lt;/strong&gt; option and press &lt;strong&gt;Edit&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Enable &lt;strong&gt;Create Users&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enable &lt;strong&gt;Update User Attributes&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Enable &lt;strong&gt;Deactivate Users&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Press &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Import users from the todo app into Okta. Navigate to the &lt;strong&gt;Import&lt;/strong&gt; tab and press the &lt;strong&gt;Import Now&lt;/strong&gt; button. Okta discovers users in your app and tries to match them with users already defined in Okta. A dialog shows Okta discovered the two users you added using the DB script. Select both users and press the &lt;strong&gt;Confirm Assignments&lt;/strong&gt; to confirm the assignments.&lt;/p&gt;

&lt;p&gt;You’ll see the imported users in the &lt;strong&gt;Assignments&lt;/strong&gt; tab. But what about entitlements? They’re coming right up!&lt;/p&gt;

&lt;p&gt;Stop the tunnel and the API using the &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;c&lt;/kbd&gt; command in the terminal windows. We’ll make some changes to the API that won’t automatically reflect in the local tunnel, so we’ll get all our entitlements changes made and resynchronize with Okta.&lt;/p&gt;

&lt;h2 id=&quot;scim-schemas-and-resources&quot;&gt;SCIM schemas and resources&lt;/h2&gt;

&lt;p&gt;In the first SCIM workshop, you learned about SCIM’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; resource and built out operations around the user. You updated only a handful of user properties in the workshop, but SCIM is way more powerful thanks to its superpower – &lt;em&gt;extensibility&lt;/em&gt;. ✨ User is not the only resource type defined in SCIM.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resource&lt;/code&gt; represents an object SCIM operates on, such as a user or group. SCIM identified core properties each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resource&lt;/code&gt; must define, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; and a link to the resource’s schema definition. From there, a user extends from the core properties and adds attributes specific to the object, such as adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userName&lt;/code&gt; and their emails. A standard published schema exists for all those user-specific attributes within the SCIM spec. You can continue extending resources as needed to represent new resources, such as another SCIM standard-defined schema for Enterprise User.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/user-entitlements-workshop/resource-class-diagram-696cc8f04feb6e62c81cc68743f76254c52319cfd1c26411d6933db9274a183d.svg&quot; alt=&quot;Class diagram representing core Resource properties, User class extending from core Resource adds username and emails properties. The Enterprise User class extends from User adds department and costCenter properties. Group class extends from core Resource and adds displayName and members properties. Other class extends from core Resource demonstrating new resource representations.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What’s an example resource other than a user or group? If you said “role” or an “entitlement,” you’re correct! Those resource types must have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schemas&lt;/code&gt;. Here, Okta used SCIM’s extensibility to define a new resource type.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/user-entitlements-workshop/resource-role-class-diagram-db8aa8eb1908e9bbbcfe2bae7f02a814ec732e4dc925ae761a629c159839dfd2.svg&quot; alt=&quot;Class diagram representing core Resource properties. The User, Group, OktaRole, and Other class extends from core Resource.&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Okta defines a schema for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; representation. We can use the schema to ensure we conform to the definition.&lt;/p&gt;

&lt;h3 id=&quot;harness-typescript-to-conform-to-scim-schemas&quot;&gt;Harness TypeScript to conform to SCIM schemas&lt;/h3&gt;

&lt;p&gt;We can define an interface to model the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; representation. Add a new file to the project named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim-types.ts&lt;/code&gt; and open it up in the IDE. This file will contain the SCIM schema definitions, such as the SCIM core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resource&lt;/code&gt;. Each interface defines required and optional properties and the property’s type.&lt;/p&gt;

&lt;p&gt;Copy and paste the first interface for the SCIM resource into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scim-types.ts&lt;/code&gt; file.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IScimResource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IMetadata&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;A SCIM resource has an optional meta property containing the resource’s metadata. Your IDE shows errors, so we can fix this by adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMetadata&lt;/code&gt; definition to the file below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IScimResource&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;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IMetadata&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;resourceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RESOURCE_TYPES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;You’ll have a new error for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_TYPES&lt;/code&gt;. We’ll fix it soon.&lt;/p&gt;

&lt;p&gt;Now, on to the Okta &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Role&lt;/code&gt; representation. The role representation extends from the core SCIM resource and adds extra properties. Okta’s schema overlaps with the SCIM standard User &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; field, which includes a property for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt; text. Define the interface and add it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IMetadata&lt;/code&gt; below.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IScimResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;IOktaRole&lt;/code&gt; extends from the core &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IScimResource&lt;/code&gt; interface and adds a new required property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;displayName&lt;/code&gt;. Each resource requires a schema, a Uniform Resource Namespace (URN) string. Instead of repeatedly typing the string for each role resource, define it below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; interface for reusability&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;SCHEMA_OKTA_ROLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:okta:scim:schemas:core:1.0:Role&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;Let’s fix the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_TYPES&lt;/code&gt; error. Below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCHEMA_OKTA_ROLE&lt;/code&gt; constant, add the following:&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;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RESOURCE_TYPES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Role&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;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; interface in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint to ensure the response matches the expected structure. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt;, and update the code to use the interface.&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;Router&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;express&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;IOktaRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&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;./scim-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;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;rolesRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;rolesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&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;p&quot;&gt;[{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schemas&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;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;one&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;displayName&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;Todo-er&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;roles&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;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Why use TypeScript and interfaces?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;TypeScript, a superset of JavaScript, supports type safety. Type safety means we’ll catch errors within the IDE or at build time instead of getting caught by surprise with a runtime error. Here, we state the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; array is of type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole[]&lt;/code&gt;. Try commenting out the required &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schemas&lt;/code&gt; property. You’ll see an error in an IDE that supports TypeScript or when you try to serve the API as console output. We can use type safety to ensure we meet the expectations of required SCIM properties in our calls.&lt;/p&gt;

  &lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/user-entitlements-workshop/type-error-e1d841636e4957ba250a1e85269ef5807987fd47bd070653fd8289d6737d20d6.jpg&quot; alt=&quot;IDE and terminal showing the type error when `schemas` is commented out&quot; width=&quot;600&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every code change deserves a quick check. Serve the API and double check everything still works for you when you make the HTTP call to&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;http://localhost:3333/scim/v2/Roles&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do you see the one ‘Todo-er’ role in the response? ✅&lt;/p&gt;

&lt;h3 id=&quot;scim-list-response&quot;&gt;SCIM list response&lt;/h3&gt;

&lt;p&gt;We return the array of Okta roles directly in the API response, but this format doesn’t match SCIM list responses. SCIM has a structured response format for lists and a defined schema. This way, SCIM structures all communication between the client and the server so each side knows how to format and parse data.&lt;/p&gt;

&lt;p&gt;Let’s define the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListResponse&lt;/code&gt; interface. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim-types.ts&lt;/code&gt;. The list response contains standard information supporting pagination, the schema for the list response, and the list of objects. Add the interface to the file. I like to organize my definitions, so I added the code between the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; interface and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCHEMA_OKTA_ROLE&lt;/code&gt; string constant.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&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 list response also has a schema URN. Create a constant for this string as you did for the Okta role and add it after the role schema string.&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;SCHEMA_LIST_RESPONSE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:ietf:params:scim:api:messages:2.0:ListResponse&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;The API response must match the list format. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt; and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResponse&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCHEMA_LIST_RESPONSE&lt;/code&gt; to the imports from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scim-types&lt;/code&gt; file:&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;IListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&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;./scim-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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rolesRoute&lt;/code&gt; response to use the list response:&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;rolesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&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;p&quot;&gt;[{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schemas&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;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;one&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;displayName&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;Todo-er&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;listResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&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;schemas&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;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;na&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;na&quot;&gt;startIndex&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;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;nx&quot;&gt;res&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;listResponse&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;Double-check everything still works. Send the HTTP request to your API. ✅&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;http://localhost:3333/scim/v2/Roles&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;return-database-defined-roles-in-the-scim-roles-endpoint&quot;&gt;Return database-defined roles in the SCIM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint&lt;/h3&gt;

&lt;p&gt;Each role has an ID and a name. We can retrieve the roles from the database and populate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt; and make the changes to retrieve the roles from the database and map the database results to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; properties. You’ll need to import some dependencies, so ensure the import statements match. The SCIM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListResponse&lt;/code&gt; supports pagination, so we’ll add the required code to consider the query parameters.&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;Router&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;express&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;PrismaClient&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;@prisma/client&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;IListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&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;./scim-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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PrismaClient&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;rolesRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;rolesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&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;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;startIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;startIndex&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;1&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;100&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;prisma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;findMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;startIndex&lt;/span&gt; &lt;span class=&quot;o&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;listResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&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;schemas&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;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;schemas&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;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;listResponse&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;Run a quick check to ensure everything still works. Serve the API and call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint using your HTTP client. ✅&lt;/p&gt;

&lt;p&gt;You should see three roles matching the roles in the database. 🎉&lt;/p&gt;

&lt;h2 id=&quot;scim-resource-types&quot;&gt;SCIM resource types&lt;/h2&gt;

&lt;p&gt;We implemented the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint and discussed how SCIM defines a resource. But how would the SCIM client know about this Okta Role type? Enter discovery—learning about a SCIM server’s capabilities and supported objects such as resources!&lt;/p&gt;

&lt;p&gt;SCIM clients and servers communicate about the types of resources through a standard endpoint, the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ResourceType&lt;/code&gt; endpoint. SCIM clients call the endpoint to discover what resources they can expect. The endpoint returns a SCIM list response outlining resources. You can add every resource type used, including the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EnterpriseUser&lt;/code&gt; resources, but Okta expects resource definitions only for custom types.&lt;/p&gt;

&lt;p&gt;First, we’ll create the interface for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResourceType&lt;/code&gt; and define some strings. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim-types.ts&lt;/code&gt;. Add the interface for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IResourceType&lt;/code&gt; above the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResponse&lt;/code&gt; interface.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;nl&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
  &lt;span class=&quot;nl&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IMetadata&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;Notice the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IResourceType&lt;/code&gt; doesn’t extend from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IScimResource&lt;/code&gt; interface. For example, the SCIM standard doesn’t require &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; for a resource type. Since the SCIM standard treats &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResourceType&lt;/code&gt; as an exception case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resource&lt;/code&gt;, we defined it separately without the relation instead of extending from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IScimResource&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When following the SCIM protocol, responses that list values, such as the list of roles or resource types, use the SCIM list response format.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResource&lt;/code&gt; interface must support &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IResourceType&lt;/code&gt;. Using &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/generics.html&quot;&gt;generics&lt;/a&gt; and &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types&quot;&gt;union types&lt;/a&gt;, we can support different list response objects . Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResource&lt;/code&gt; to match the code below.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IScimResource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&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;na&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&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;You’ll see errors in the IDE and, if you’re running the API, within the console output. No worries; we’ll fix those errors soon!&lt;/p&gt;

&lt;p&gt;Resource types have a schema URN and use “ResourceType” as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resourceType&lt;/code&gt; string in the metadata. Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCHEMA_RESOURCE_TYPE&lt;/code&gt; and edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_TYPES&lt;/code&gt; so your string constants section looks like the code below.&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;SCHEMA_OKTA_ROLE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:okta:scim:schemas:core:1.0:Role&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;SCHEMA_LIST_RESPONSE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:ietf:params:scim:api:messages:2.0:ListResponse&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;SCHEMA_RESOURCE_TYPE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:ietf:params:scim:schemas:core:2.0:ResourceType&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;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RESOURCE_TYPES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ResourceType&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;Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt;. Let’s fix the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResponse&lt;/code&gt; error for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt; endpoint and specify the object type in the list, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IOktaRole&lt;/code&gt; type. The code building out the list changes to&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;listResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;schemas&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;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;schemas&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;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&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;You shouldn’t see errors anymore! 🎉&lt;/p&gt;

&lt;p&gt;We have a new endpoint to add. Update the imports from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./scim-types&lt;/code&gt; file and declare a new route for resource types.&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;Router&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;express&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;PrismaClient&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;@prisma/client&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;IListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_RESOURCE_TYPE&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;./scim-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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PrismaClient&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;rolesRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&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;resourceTypesRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// existing rolesRoute code below&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then create the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ResourceTypes&lt;/code&gt; route by adding the code below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rolesRoute&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;nx&quot;&gt;resourceTypesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;resourceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&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;p&quot;&gt;[{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schemas&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;SCHEMA_RESOURCE_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;Role&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;name&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;Role&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;endpoint&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;/Roles&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;description&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;Roles you can set on users of Todo App&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;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;meta&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;resourceType&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;ResourceType&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;resourceTypesListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;schemas&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;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTypes&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;na&quot;&gt;startIndex&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;na&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTypes&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;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTypes&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;nx&quot;&gt;res&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;resourceTypesListResponse&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;Next, you must register the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ResourceTypes&lt;/code&gt; route in the API. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update the import to include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resourceTypesRoute&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;resourceTypesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rolesRoute&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;./entitlements&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;Add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ResourceTypes&lt;/code&gt; endpoint to the end of the file. You should have two routes defined.&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;scimRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&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;/Roles&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;rolesRoute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;scimRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&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;/ResourceTypes&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;resourceTypesRoute&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;Double-check your new route by starting the API if it’s not running. Use your HTTP client to make the call&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;http://localhost:3333/scim/v2/ResourceTypes&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you see a response with the Okta role resource type, the API call works as expected! ✅&lt;/p&gt;

&lt;h2 id=&quot;add-roles-to-the-scim-users-endpoints&quot;&gt;Add roles to the SCIM Users endpoints&lt;/h2&gt;

&lt;p&gt;Let’s add roles to the existing user calls. We want to reflect a user’s roles in Okta within the Todo app, so the GET and POST &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users&lt;/code&gt; calls must support roles. Near the top of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scim.ts&lt;/code&gt; file, find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IUserSchema&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;Update the interface to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; property:&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IUserSchema&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;userName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&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;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;familyName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;nl&quot;&gt;emails&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;primary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;locale&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;meta&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;resourceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;nl&quot;&gt;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;groups&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;nl&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;detail&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;roles&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;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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 User SCIM schema defines &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; property as a list of objects that may contain properties named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;display&lt;/code&gt;, among others. Okta uses these properties for role data.&lt;/p&gt;

&lt;h3 id=&quot;update-the-scim-add-users-call-to-include-roles&quot;&gt;Update the SCIM add users call to include roles&lt;/h3&gt;

&lt;p&gt;The first route defined is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /Users&lt;/code&gt; route definition. You need to add roles when saving to the database. Find the comment&lt;/p&gt;

&lt;div class=&quot;language-js 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 the User in the database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and update the database command and the 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;c1&quot;&gt;// Create the User in the database&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&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;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;data&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;org&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;connect&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;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ORG_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}},&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;roles&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;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;newUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;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;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;include&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;roles&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;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&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;Account Created ID: &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;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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One more place to update in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST /Users&lt;/code&gt; call. We need to return the roles in the response. Right below the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log()&lt;/code&gt; update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userResponse&lt;/code&gt; to&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;userResponse&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;nx&quot;&gt;defaultUserSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;id&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;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;s2&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;userName&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;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&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;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;familyName&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;emails&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;primary&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;value&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;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;externalId&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;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;active&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;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&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;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&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;add-roles-when-getting-a-list-of-users-in-scim&quot;&gt;Add roles when getting a list of users in SCIM&lt;/h3&gt;

&lt;p&gt;Continuing to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET /Users&lt;/code&gt; call, search for the code to find users in the database&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;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&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;findMany&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;to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;select&lt;/code&gt; argument.&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;users&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;prisma&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;findMany&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;recordLimit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;select&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;id&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;email&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;name&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;externalId&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;active&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;roles&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;nx&quot;&gt;where&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;GET /Users&lt;/code&gt; response also needs roles, so update the&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;usersResponse&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;Resources&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;o&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;nx&quot;&gt;map&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;=&amp;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;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;nx&quot;&gt;usersResponse&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;Resources&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;o&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;nx&quot;&gt;map&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;=&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;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;familyName&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;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;dl&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;defaultUserSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;userName&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;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&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;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;familyName&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;emails&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;primary&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;value&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;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&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;work&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;displayName&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;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;externalId&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;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;active&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;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;roles&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;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&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;h4 id=&quot;update-the-response-for-an-individual-user&quot;&gt;Update the response for an individual user&lt;/h4&gt;

&lt;p&gt;On to the next call, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET /Users/:userId&lt;/code&gt;. We need to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; to the&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;kd&quot;&gt;const&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&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;findFirst&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;database command. Update it to match the code below.&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;kd&quot;&gt;const&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;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;prisma&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;findFirst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;select&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;id&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;email&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;name&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;externalId&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;active&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;roles&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;where&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;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;org&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;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ORG_ID&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;Then, find the comment&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;c1&quot;&gt;// If no response from DB, return 404&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userResponse&lt;/code&gt; object inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if&lt;/code&gt; statement. Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userResponse&lt;/code&gt; to match the code 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;nx&quot;&gt;userResponse&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;nx&quot;&gt;defaultUserSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;id&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;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;userName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&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;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;familyName&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;emails&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;primary&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;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&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;work&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;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;externalId&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;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;active&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;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&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;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&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;satisfies&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IUserSchema&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;update-the-users-call-so-scim-clients-can-set-their-roles&quot;&gt;Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users&lt;/code&gt; call so SCIM clients can set their roles&lt;/h3&gt;

&lt;p&gt;Another endpoint down, but there’s one more left, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT /Users/:userId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Find the code&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;emails&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;updatedUserRequest&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;and change it to the following code so we can work with the user’s updated roles and save the changes in the database.&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;emails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&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;updatedUserRequest&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;updatedUser&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;prisma&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;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;data&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;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;emails&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;primary&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;na&quot;&gt;name&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;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;givenName&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;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;familyName&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;na&quot;&gt;roles&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;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;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;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;where&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;id&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;include&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;roles&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lastly, we need to update the response from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT /Users/:userId&lt;/code&gt; call. Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userResponse&lt;/code&gt; object to look 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;nx&quot;&gt;userResponse&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;nx&quot;&gt;defaultUserSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;id&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;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;userName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&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;givenName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;familyName&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;emails&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;primary&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;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&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;work&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;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;externalId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;active&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;updatedUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;role&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;na&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;role&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;nx&quot;&gt;toString&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;satisfies&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IUserSchema&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;Serve the API if you aren’t running it using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run serve-api&lt;/code&gt;. Let’s make an HTTP call to get all users to double-check our work.&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;GET http://localhost:3333/scim/v2/Users
Authorization: Bearer 123123
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will see the list of users. Each user object has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements&lt;/code&gt; property. ✅&lt;/p&gt;

&lt;h2 id=&quot;entitlements-discovery-in-okta&quot;&gt;Entitlements discovery in Okta&lt;/h2&gt;

&lt;p&gt;What can Okta do with user entitlements? Okta can discover defined entitlements, such as the roles you define for the Todo app, and applies existing roles on users. Now that you have all the endpoints needed for a SCIM client to discover resources held by a SCIM server, you can see this in action on Okta.&lt;/p&gt;

&lt;p&gt;You’ll need to serve the API and create a local tunnel. Serve the API using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run serve-api&lt;/code&gt; command. In a second terminal window, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx localtunnel --port 3333&lt;/code&gt;. Take note of your tunnel URL.&lt;/p&gt;

&lt;p&gt;Sign into your &lt;a href=&quot;https://developer.okta.com/login/&quot;&gt;Okta Developer Edition account&lt;/a&gt;. Navigate to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt; and select the “(Header Auth) Governance with SCIM 2.0” app. Navigate to the &lt;strong&gt;Provisioning&lt;/strong&gt; tab and select &lt;strong&gt;Integration&lt;/strong&gt;. Press &lt;strong&gt;Edit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Update the &lt;strong&gt;Base URL&lt;/strong&gt; field by replacing the tunnel URL with your new tunnel URL. Make sure you keep the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/scim/v2&lt;/code&gt; path. Your base URL might look something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://beep-bop-boop.loca.lt/scim/v2&lt;/code&gt;. Press &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Updating the API integration kicks off a discovery process. Okta automatically looks for roles as a possible entitlement type. It then matches the roles it discovers for the Todo application and matches them again with roles defined on the users. You can see Okta working by looking at the terminal window serving the API. You can see the calls Okta makes by inspecting the HTTP requests and their payloads written to the console.  🔍&lt;/p&gt;

&lt;p&gt;Make sure to keep the API running! There’s more work to do here!&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Governance&lt;/strong&gt; tab. The tab you see is &lt;strong&gt;Entitlements&lt;/strong&gt;. Do you see &lt;strong&gt;Role&lt;/strong&gt; in the sidenav below the &lt;strong&gt;Search&lt;/strong&gt; input? If not, hang tight. Because an app may have many defined entitlements, Okta starts a background job to discover roles asynchronously. It could take up to 10 minutes for the roles to populate.&lt;/p&gt;

&lt;p&gt;Eventually, you’ll see &lt;strong&gt;Role&lt;/strong&gt;; when you select it, you’ll see metadata about it, such as the variable name, data type, and description. We also see the values: “Manager,” “Todo Auditor,” and “Todo-er.”&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/user-entitlements-workshop/entitlements-roles-7344da8e7387c37c7b6f3c9d16abcbf326e6477fe02ebb172b2eaf2666ee2958.jpg&quot; alt=&quot;Governance tab with roles discovered by Okta&quot; width=&quot;800&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can define policies for users that automatically assign their entitlements when adding them to this integration app. While that’s pretty nifty, this post focuses on building out the SCIM endpoints for entitlements, so I’ll include links to resources that explain this feature in more detail at the end of the post.&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;&amp;lt; Back to application&lt;/strong&gt; to return to the SCIM Okta app.&lt;/p&gt;

&lt;h3 id=&quot;syncing-user-entitlements&quot;&gt;Syncing user entitlements&lt;/h3&gt;

&lt;p&gt;When you use an identity provider, you want that system to be the source of truth for managing the users’ identities and access levels. You want to set the roles you defined for the Todo app onto users within Okta. That would be pretty sweet, right?&lt;/p&gt;

&lt;p&gt;Since we last ran our user import with hardcoded roles, let’s ensure we’ve synchronized everything from the starting state of the application before we start managing with Okta.&lt;/p&gt;

&lt;p&gt;Within the SCIM application tab, navigate to &lt;strong&gt;Import&lt;/strong&gt; and press the &lt;strong&gt;Import Now&lt;/strong&gt; button. Okta scans the users in the todo app, but since there are no new users, there’s no confirmation process. The user scan synced the existing users and the roles!&lt;/p&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Assignments&lt;/strong&gt;. Each user has a vertical 3-dot menu icon to display a context menu allowing you to &lt;strong&gt;Edit user assignment&lt;/strong&gt;, &lt;strong&gt;View access details **, and **Unassign&lt;/strong&gt;. Find “Trinity” and **View access details ** on them. A panel shows you Trinity’s role pre-assigned in the Todo app. 🎉 Exit the side panel by clicking outside the side panel.&lt;/p&gt;

&lt;p&gt;Let’s assign a new role to “Somnus” using Okta.  Open the context menu for “Somnus” and &lt;strong&gt;View access details&lt;/strong&gt;. Press the &lt;strong&gt;Edit access&lt;/strong&gt; button. You’ll see a page titled &lt;strong&gt;Edit access&lt;/strong&gt;. Press the &lt;strong&gt;Customize entitlements&lt;/strong&gt; button. You’ll see a warning followed by a section called &lt;strong&gt;Custom Entitlements&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You’ll see &lt;strong&gt;Role&lt;/strong&gt; and a dropdown list with values. Select a role, such as “Todo-er,” and press &lt;strong&gt;Save&lt;/strong&gt; to add the role to the user.&lt;/p&gt;

&lt;p&gt;But how about the Todo app? Take a look at the terminal output where you’re serving the API. The HTTP call tracing shows a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; request on the user adding the role. Can you see the role of the user in the database? You can check it out by opening another terminal window, running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx prisma studio&lt;/code&gt;, and navigating to the website. ✅&lt;/p&gt;

&lt;p&gt;You can now use Okta to manage user roles centrally and automatically update the user’s grants!&lt;/p&gt;

&lt;p&gt;Stop serving the local tunnel and API for this next section.&lt;/p&gt;

&lt;h3 id=&quot;schema-discovery-for-custom-entitlements&quot;&gt;Schema discovery for custom entitlements&lt;/h3&gt;

&lt;p&gt;What if we have something other than roles in the application? Can SCIM support custom entitlement strategies? SCIM is extensible, meaning it has the structure for custom schemas and extends beyond the core resources. A SCIM server can publish a custom schema if it defines custom resource types.&lt;/p&gt;

&lt;p&gt;Let’s say you have user roles but want to add a custom entitlement, such as licenses, profiles, or something else. Let’s walk through the example where we want to add a custom entitlement. We will call this “Characteristic,” such as whether the user is tall. We know Trinity is tall, so it’s logical to note their tallness as part of their user attributes.&lt;/p&gt;

&lt;p&gt;SCIM clients must discover resources through schemas. So, we first need to define the schema describing “Characteristics.” Note that I came up with “Characteristics” as the name of this attribute, but you will need to change it for your user entitlements model, whether it be some sort of permissions system or something else. Custom schemas can extend from an existing schema, such as Okta’s entitlement schema, which tracks data as a key-value pair, and add our own flavoring to it.&lt;/p&gt;

&lt;p&gt;In the IDE, open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim-types.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add new schema URNs after the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCHEMA_OKTA_ROLE&lt;/code&gt; definition towards the end of the file:&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;SCHEMA_OKTA_ENTITLEMENT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:okta:scim:schemas:core:1.0:Entitlement&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;SCHEMA_CHARACTERISTIC&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;urn:bestapps:scim:schemas:extension:todoapp:1.0:Characteristic&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;We defined a new schema URN for the characteristic SCIM resource. Following naming conventions for extension schemas, we substituted our company name (Best Apps) and added the app’s name (Todo app). The format looks like this&lt;/p&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;urn:&amp;lt;Company name&amp;gt;:scim:schemas:extension:&amp;lt;App name&amp;gt;:1.0:&amp;lt;Custom entitlement&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Right now, there’s a custom TypeScript type for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RESOURCE_TYPES&lt;/code&gt;. Since we’ll have custom schemas as a resource type, update the code.&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;type&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;RESOURCE_TYPES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Role&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;ResourceType&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Schema&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;SCIM defines required and optional attributes to describe a schema resource. We’ll define the interfaces for a schema resource. Add the following interfaces to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scim-types.ts&lt;/code&gt; file. I added mine after the other interfaces and before the URNs.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ISchema&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IAttribute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IMetadata&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IAttribute&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;multiValued&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;caseExact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;mutability&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;returned&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;uniqueness&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;Characteristic is a unique resource type because it’s a new, custom type extending from an existing schema. We must explicitly show this relationship for consuming SCIM clients, like Okta. Find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IResourceType&lt;/code&gt; interface. We’ll add a new optional property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;schemaExtensions&lt;/code&gt; and inline the type 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;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;schemaExtensions&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;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IMetadata&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;SCIM clients expect a list of schemas that you offer in the SCIM server. You might’ve guessed what that means. You must wrap all the schemas in a SCIM &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListResponse&lt;/code&gt;. Find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResponse&lt;/code&gt; and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ISchema&lt;/code&gt; as a supported type. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IListResponse&lt;/code&gt; interface changes to:&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IScimResource&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ISchema&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;na&quot;&gt;schemas&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;totalResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;startIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;itemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;T&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;Finally, we define what a characteristic attribute looks like by adding the interface shown below.&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;kr&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ICharacteristic&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IScimResource&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&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;With all the types and interfaces defined, it’s time to write the code for the route. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/entitlements.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Update the import array from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./scim-types.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;ICharacteristic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;IOktaRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;IResourceType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;ISchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;SCHEMA_CHARACTERISTIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ENTITLEMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;SCHEMA_RESOURCE_TYPE&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;./scim-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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Below the other route definitions, add two new route definitions.&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;schemasRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&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;characteristicsRoute&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Router&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, it’s time to define the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Schemas&lt;/code&gt; route. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Schemas&lt;/code&gt; endpoint returns a list of schemas. You can return schemas for all the resources you use, even for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt;, but Okta allows us to skip the strict SCIM requirements and only return custom schemas. The custom schema we’ll return has metadata about a user characteristic, specifically whether the user is tall. Add the following code at the end of the file.&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;schemasRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;characteristic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ISchema&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;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_CHARACTERISTIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&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;Characteristic&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;description&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 characteristics for entitlements&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;attributes&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;name&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;is_tall&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;description&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 entitlement extension for tallness factor&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;type&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;string&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;multiValued&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;mutability&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;readWrite&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;returned&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;default&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;caseExact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uniqueness&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;none&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;meta&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;resourceType&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;Schema&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;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`/v2/Schemas/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;SCHEMA_CHARACTERISTIC&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;schemas&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;schemas&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;SCHEMA_LIST_RESPONSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;totalResults&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;na&quot;&gt;startIndex&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;na&quot;&gt;itemsPerPage&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;na&quot;&gt;Resources&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;characteristic&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;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;schemas&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;And we must define a route for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Characteristics&lt;/code&gt;, in the same way one exists for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Roles&lt;/code&gt;. We won’t worry about updating the database for this as I don’t want to detract from the SCIM concepts. We’ll hardcode the characteristic for now so you can see what this looks like within Okta. Feel free to add the required code to connect it to the database as homework. 🏆 Add the following code below the schemas 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;nx&quot;&gt;characteristicsRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;route&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;/&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;kd&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;characteristicsListResponse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IListResponse&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ICharacteristic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&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;schemas&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;SCHEMA_OKTA_ENTITLEMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;SCHEMA_CHARACTERISTIC&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;totalResults&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;na&quot;&gt;startIndex&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;na&quot;&gt;itemsPerPage&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;na&quot;&gt;Resources&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;schemas&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;SCHEMA_CHARACTERISTIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Characteristic&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;is_tall&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;displayName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;This user is so tall&lt;/span&gt;&lt;span class=&quot;dl&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;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&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;characteristicsListResponse&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;Notice the ID is the string “is_tall”. I modeled it to look like an enum here so that it’s distinct from roles, but IDs in your system may be a UUID or an integer.&lt;/p&gt;

&lt;p&gt;Lastly, we must add the new characteristic resource type to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/ResourceTypes&lt;/code&gt; response so that Okta knows the resource exists. Find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resourceTypes.route('/')&lt;/code&gt; definition and update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resourceTypes&lt;/code&gt; array to include both roles and characteristics.&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;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTypes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;IResourceType&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;p&quot;&gt;[{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schemas&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;SCHEMA_RESOURCE_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;Role&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;name&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;Role&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;endpoint&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;/Roles&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;description&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;Roles you can set on users of Todo App&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;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_OKTA_ROLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;meta&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;resourceType&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;ResourceType&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;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;schemas&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;SCHEMA_RESOURCE_TYPE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;id&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;Characteristic&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;name&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;Characteristic&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;endpoint&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;/Characteristics&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;description&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;This resource type is user characteristics&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;schema&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;urn:okta:scim:schemas:core:1.0:Entitlement&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;schemaExtensions&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;na&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;SCHEMA_CHARACTERISTIC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;required&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;na&quot;&gt;meta&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;resourceType&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;ResourceType&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;p&quot;&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, we must register the routes in the API. Open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;okta-enterprise-ready-workshops/apps/api/src/scim.ts&lt;/code&gt;. At the top of the file, update the imports from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./entitlements&lt;/code&gt; to&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;characteristicsRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;resourceTypesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rolesRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;schemasRoute&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;./entitlements&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;At the end of the file, add the code to register the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Schemas&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Charactertistics&lt;/code&gt; routes to the API.&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;scimRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&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;/Schemas&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;schemasRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;scimRoute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;use&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;/Characteristics&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;characteristicsRoute&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;Serve the API by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm run serve-api&lt;/code&gt; in a terminal window. In a second terminal window, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx localtunnel --port 3333&lt;/code&gt; to create a local tunnel for the API. Keep track of the tunnel URL.&lt;/p&gt;

&lt;p&gt;Back in the &lt;a href=&quot;https://developer.okta.com/login/&quot;&gt;Okta Admin console&lt;/a&gt;, navigate to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt; and open the SCIM with governance Okta app. Navigate to &lt;strong&gt;Provisioning&lt;/strong&gt; &amp;gt; &lt;strong&gt;Integration&lt;/strong&gt;. Press &lt;strong&gt;Edit&lt;/strong&gt; and update the &lt;strong&gt;Base URL&lt;/strong&gt; using the new tunnel URL. Don’t forget to keep the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/scim/v2&lt;/code&gt; at the end of the URL. The URL should look something like&lt;/p&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;https://{yourTunnelSubdomain}.loca.lt/scim/v2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Press &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Okta discovers schemas and resource types when updating the provisioning configuration. If you look at the HTTP call tracing in the terminal window serving the API, you’ll see that Okta made a GET request to both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Schemas&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Characteristics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;strong&gt;Governance&lt;/strong&gt;. &lt;strong&gt;Characteristic&lt;/strong&gt; may take 10-15 minutes to populate, but you’ll see the display name and value when it does. Go &lt;strong&gt;&amp;lt; Back to application&lt;/strong&gt; and navigate to &lt;strong&gt;Assignments&lt;/strong&gt;. Open the user context menu for “Trinity” by pressing the three vertical dots icon menu and opening &lt;strong&gt;View entitlements&lt;/strong&gt;. Press &lt;strong&gt;Edit&lt;/strong&gt; and &lt;strong&gt;Customize entitlements&lt;/strong&gt; to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_tall&lt;/code&gt; user characteristic. &lt;strong&gt;Save&lt;/strong&gt; the changes and navigate back to the Okta SCIM app.&lt;/p&gt;

&lt;p&gt;Check out the terminal serving the API for the HTTP call tracing. You’ll see a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PUT&lt;/code&gt; request on Trinity adding the new characteristic. The field goes into the core SCIM User &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements&lt;/code&gt; property. Check it out by inspecting the HTTP tracing in the console output. ✅&lt;/p&gt;

&lt;h2 id=&quot;multi-tenant-use-cases-for-entitlements&quot;&gt;Multi-tenant use cases for entitlements&lt;/h2&gt;

&lt;p&gt;In this workshop, we defined roles for the entire Todo app. But what if your SaaS app supports tenant-configurable roles? You must make structural changes to the Todo app database to support organization roles. Notice that an organization has a unique API key, and we included this API as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bearer&lt;/code&gt; token value in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorization&lt;/code&gt; header. All the SCIM calls from Okta can target a specific organization in the Todo app, including the organization’s custom roles.&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;Note&lt;/strong&gt; &lt;br /&gt;
      We used an API key for demonstration purposes, but we recommend using OAuth to secure the calls from Okta to your API for production applications.
    &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id=&quot;use-scim-to-manage-user-provisioning-and-entitlements&quot;&gt;Use SCIM to manage user provisioning and entitlements&lt;/h2&gt;

&lt;p&gt;In this workshop, you dived deeper into SCIM and learned about resources and schemas. You also synced users and their pre-existing entitlements from the Todo app and provisioned users within Okta. I hope you enjoyed this workshop and have ideas for using it for your SaaS applications! Check out the &lt;a href=&quot;https://help.okta.com/oie/en-us/content/topics/identity-governance/iga.htm&quot;&gt;Identity Governance&lt;/a&gt; help docs to learn about Okta Identity Governance.&lt;/p&gt;

&lt;p&gt;You can find the completed code project in the &lt;a href=&quot;https://github.com/oktadev/okta-enterprise-ready-workshops/tree/entitlements-workshop-complete&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entitlements-workshop-completed&lt;/code&gt; branch within the GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to learn more about what it means to be enterprise-ready and to have enterprise maturity, check out the other workshops in this series&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Posts in the on-demand workshop series&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;1. &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;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2. &lt;a href=&quot;/blog/2023/07/28/oidc_workshop&quot;&gt;Enterprise-Ready Workshop: Authenticate with OpenID Connect&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3. &lt;a href=&quot;/blog/2023/07/28/scim-workshop&quot;&gt;Enterprise-Ready Workshop: Manage Users with SCIM&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;4. &lt;a href=&quot;/blog/2023/07/28/terraform-workshop&quot;&gt;Enterprise Maturity Workshop: Terraform&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;5. &lt;a href=&quot;/blog/2023/09/15/workflows-workshop&quot;&gt;Enterprise Maturity Workshop: Automate with no-code Okta Workflows&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6. &lt;a href=&quot;/blog/2024/04/30/express-universal-logout&quot;&gt;How to Instantly Sign a User Out across All Your Apps&lt;/a&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7. &lt;strong&gt;Take User Provisioning to the Next Level with Entitlements&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Want to learn about more exciting topics? Let us know by commenting below. To get notified about exciting new content, follow us on &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://www.youtube.com/c/oktadev&quot;&gt;YouTube&lt;/a&gt; channel.&lt;/p&gt;
</description>
        <pubDate>Wed, 21 Jan 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/01/21/user-entitlements-workshop</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/01/21/user-entitlements-workshop</guid>
      </item>
    
      <item>
        <title>Introducing xaa.dev: A Playground for Cross App Access</title>
        <description>&lt;p&gt;AI agents are quickly becoming part of everyday enterprise development. They summarize emails, coordinate calendars, query internal systems, and automate workflows across tools.&lt;/p&gt;

&lt;p&gt;But once an AI agent needs to access an enterprise application &lt;em&gt;on behalf of a user&lt;/em&gt;, things get complicated.&lt;/p&gt;

&lt;p&gt;How do you securely let an AI-powered app act for a user without exposing credentials, spamming consent prompts, or losing administrative control?&lt;/p&gt;

&lt;p&gt;This is the problem &lt;strong&gt;Cross App Access (XAA)&lt;/strong&gt; is designed to solve.&lt;/p&gt;

&lt;p&gt;Today, we’re introducing &lt;strong&gt;&lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt;&lt;/strong&gt;, a free, open playground that lets you explore Cross App Access end-to-end. &lt;strong&gt;No local setup. No infrastructure to provision.&lt;/strong&gt; Just a working environment where you can see the protocol in action.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/xaa-dev-playground/xaa-dev-homepage-1ced782c627da4675bc483f0b21b24dbb583b30b0fc3978cf753233df80a5cda.jpg&quot; alt=&quot;xaa.dev playground homepage showing the Cross App Access flow&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; xaa.dev is currently in beta. We’re actively developing new features for the next release, and your feedback helps shape what comes next.&lt;/p&gt;
&lt;/blockquote&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&quot; id=&quot;markdown-toc-what-is-cross-app-access&quot;&gt;What is Cross App Access?&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#the-problem-testing-xaa-is-hard&quot; id=&quot;markdown-toc-the-problem-testing-xaa-is-hard&quot;&gt;The problem: testing XAA is hard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#what-you-can-do-on-xaadev&quot; id=&quot;markdown-toc-what-you-can-do-on-xaadev&quot;&gt;What you can do on xaa.dev&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#requesting-app&quot; id=&quot;markdown-toc-requesting-app&quot;&gt;Requesting App&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#resource-app&quot; id=&quot;markdown-toc-resource-app&quot;&gt;Resource App&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#identity-provider&quot; id=&quot;markdown-toc-identity-provider&quot;&gt;Identity Provider&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#resource-mcp-server&quot; id=&quot;markdown-toc-resource-mcp-server&quot;&gt;Resource MCP Server&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#bring-your-own-requesting-app&quot; id=&quot;markdown-toc-bring-your-own-requesting-app&quot;&gt;Bring your own Requesting App&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#how-to-get-started&quot; id=&quot;markdown-toc-how-to-get-started&quot;&gt;How to get started&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#why-we-built-a-testing-site-for-cross-app-access&quot; id=&quot;markdown-toc-why-we-built-a-testing-site-for-cross-app-access&quot;&gt;Why we built a testing site for cross app access&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#inspect-the-xaa-flow&quot; id=&quot;markdown-toc-inspect-the-xaa-flow&quot;&gt;Inspect the XAA flow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#learn-more&quot; id=&quot;markdown-toc-learn-more&quot;&gt;Learn more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Cross App Access refers to a typical enterprise pattern: &lt;strong&gt;one application accesses another application’s resources on behalf of a user.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An internal AI assistant fetching updates from a project management system&lt;/li&gt;
  &lt;li&gt;A workflow engine booking meetings through a calendar API&lt;/li&gt;
  &lt;li&gt;An agent querying internal data sources to complete a task&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditionally, OAuth consent flows handle this. That approach works well for consumer-based apps, but it creates friction in enterprise environments where organizations require workforce oversight:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Applications and their access levels are centrally managed&lt;/li&gt;
  &lt;li&gt;IT teams need visibility into trust relationships&lt;/li&gt;
  &lt;li&gt;Access must be revocable without user involvement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cross App Access shifts responsibility from end users to the enterprise identity layer.&lt;/p&gt;

&lt;p&gt;Instead of prompting users for consent, the &lt;strong&gt;Identity Provider (IdP)&lt;/strong&gt; issues a signed identity assertion called an &lt;strong&gt;ID-JAG (Identity JWT Authorization Grant)&lt;/strong&gt;. This assertion cryptographically represents the user and the requesting application. Resource applications trust the IdP’s assertion and issue access accordingly.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No interactive consent screens making application access seamless for employees&lt;/li&gt;
  &lt;li&gt;Clear, auditable trust boundaries&lt;/li&gt;
  &lt;li&gt;Complete administrative control over app-to-app access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a deeper dive into why this matters for enterprise AI, read more about Cross App Access in this post:&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/2025/06/23/enterprise-ai&quot; style=&quot;font-size: 1.375em; margin-bottom: 20px;&quot;&gt;
                &lt;span&gt;Integrate Your Enterprise AI Tools with Cross-App Access&lt;/span&gt;
              &lt;/a&gt;
              &lt;p&gt;Manage user and non-human identities, including AI in the enterprise with Cross App Access&lt;/p&gt;
              &lt;div&gt;&lt;div class=&quot;BlogPost-attribution&quot;&gt;
            &lt;a href=&quot;/blog/authors/semona-igama/&quot;&gt;
              &lt;img src=&quot;/assets-jekyll/avatar-semona-igama-03eb4c28aca3765f862b574e032d32f6f8186d04ae9f0db75bed9c74f48a9a3f.jpg&quot; alt=&quot;avatar-avatar-semona-igama.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/semona-igama/&quot;&gt;Semona Igama&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;&lt;/div&gt;
          &lt;/article&gt;

&lt;h2 id=&quot;the-problem-testing-xaa-is-hard&quot;&gt;The problem: testing XAA is hard&lt;/h2&gt;

&lt;p&gt;XAA is built on an emerging OAuth extension called the &lt;a href=&quot;https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant&quot;&gt;Identity Assertion JWT Authorization Grant&lt;/a&gt; – an IETF draft that Okta, along with public and industry contributors, has been actively contributing to. It’s powerful, but it’s also new, and new protocols need experimentation.&lt;/p&gt;

&lt;p&gt;Here’s the challenge: to test XAA locally, you’d need to spin up:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An Identity Provider (IdP)&lt;/li&gt;
  &lt;li&gt;An Authorization Server for the resource application&lt;/li&gt;
  &lt;li&gt;The resource API itself&lt;/li&gt;
  &lt;li&gt;A requesting application (the agent or client app)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s hours (or days) of configuration before you can even see a single token exchange. Most developers give up before getting to the interesting part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;xaa.dev changes that.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We pre-configured all the components so you can focus on understanding the flow, not debugging dev environments. Go from zero to a working XAA token exchange in under 60 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://xaa.dev&quot;&gt;Launch the playground&lt;/a&gt;&lt;/strong&gt;. It’s free and requires no signup.&lt;/p&gt;

&lt;h2 id=&quot;what-you-can-do-on-xaadev&quot;&gt;What you can do on xaa.dev&lt;/h2&gt;

&lt;p&gt;The playground gives you hands-on access to every role in the Cross App Access flow:&lt;/p&gt;

&lt;h3 id=&quot;requesting-app&quot;&gt;Requesting App&lt;/h3&gt;
&lt;p&gt;Step into the shoes of an AI agent or client application. Authenticate a user, request an ID-JAG from the IdP, and exchange it for an access token at the resource server.&lt;/p&gt;

&lt;h3 id=&quot;resource-app&quot;&gt;Resource App&lt;/h3&gt;
&lt;p&gt;See the other side of the transaction. Watch how a resource server validates the identity assertion, verifies the trust relationship, and issues scoped access tokens.&lt;/p&gt;

&lt;h3 id=&quot;identity-provider&quot;&gt;Identity Provider&lt;/h3&gt;
&lt;p&gt;We’ve built a simulated IdP with pre-configured test users. Log in, see how ID-JAGs are minted, and inspect the cryptographic claims that make XAA secure.&lt;/p&gt;

&lt;h3 id=&quot;resource-mcp-server&quot;&gt;Resource MCP Server&lt;/h3&gt;
&lt;p&gt;Connect your AI agents using the Model Context Protocol (MCP). The playground provides a ready-to-use MCP server that acts as a resource application, letting you test how AI agents can securely access protected resources through the Cross App Access flow.&lt;/p&gt;

&lt;h3 id=&quot;bring-your-own-requesting-app&quot;&gt;Bring your own Requesting App&lt;/h3&gt;
&lt;p&gt;The built-in Requesting App is great for learning, but the real power comes when you test with your own application, whether it’s a traditional app or an MCP client. &lt;a href=&quot;https://xaa.dev/developer/register&quot;&gt;Register a client&lt;/a&gt; on the playground, grab the configuration, and integrate it into your local app. This lets you validate your XAA implementation against a working IdP and Resource App without spinning up your own infrastructure. The &lt;a href=&quot;https://xaa.dev/docs&quot;&gt;playground documentation&lt;/a&gt; walks you through the setup step-by-step.&lt;/p&gt;

&lt;h2 id=&quot;how-to-get-started&quot;&gt;How to get started&lt;/h2&gt;

&lt;p&gt;Getting started with xaa.dev takes less than a minute:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Open the playground&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visit &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt;. No account required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Explore the components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The playground has three components (Requesting App, Resource App, and Identity Provider), each with its own URL. Visit any component to see its configuration and understand how it participates in the XAA flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Follow the guided flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Walk through the four steps of the XAA flow: User Authentication (SSO), Token Exchange, Access Token Request, and Access Resource. Inspect the requests and responses at each step to see exactly how XAA works under the hood.&lt;/p&gt;

&lt;p&gt;That’s it. No local tools installations, Docker containers, environment variables, or CORS headaches.&lt;/p&gt;

&lt;p&gt;Watch this walkthrough video of the playground if you’d like a guided tour:&lt;/p&gt;

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

&lt;h2 id=&quot;why-we-built-a-testing-site-for-cross-app-access&quot;&gt;Why we built a testing site for cross app access&lt;/h2&gt;

&lt;p&gt;XAA is built on an emerging IETF specification, the Identity Assertion JWT Authorization Grant. As enterprise AI adoption accelerates, there’s a clear need: developers want to understand XAA, but the barrier to entry is too high.&lt;/p&gt;

&lt;p&gt;xaa.dev lowers the barrier. It helps you:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Learn faster&lt;/strong&gt; – See the protocol in action before writing any code&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Build confidently&lt;/strong&gt; – Understand exactly what tokens to expect and validate&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Experiment safely&lt;/strong&gt; – Test edge cases without affecting production systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;inspect-the-xaa-flow&quot;&gt;Inspect the XAA flow&lt;/h2&gt;

&lt;p&gt;XAA is how enterprise applications will securely connect in an AI-first world. Whether you’re building agents, integrating SaaS tools, or just curious about modern OAuth patterns, &lt;a href=&quot;https://xaa.dev&quot;&gt;xaa.dev&lt;/a&gt; gives you a risk-free environment to learn. Check it out and let us know how it works for you!&lt;/p&gt;

&lt;h2 id=&quot;learn-more&quot;&gt;Learn more&lt;/h2&gt;

&lt;p&gt;Ready to go deeper? Check out these resources:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.okta.com/integrations/cross-app-access/&quot;&gt;Checkout Cross App Access Integration in Okta &lt;/a&gt; – Securing AI-driven access together&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/blog/2025/09/03/cross-app-access&quot;&gt;Build Secure Agent-to-App Connections with Cross App Access&lt;/a&gt; – Hands-on implementation guide&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant&quot;&gt;Identity Assertion JWT Authorization Grant (IETF Draft)&lt;/a&gt; – The specification behind XAA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have questions or feedback? Reach out to us on &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt;, join the conversation on the &lt;a href=&quot;https://devforum.okta.com/&quot;&gt;Okta Developer Forums&lt;/a&gt;, or drop a comment below. We’re actively improving xaa.dev based on developer input – your feedback shapes what we build next.&lt;/p&gt;

&lt;p&gt;Follow us on &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href=&quot;https://youtube.com/oktadev&quot;&gt;YouTube channel&lt;/a&gt; for more content on identity, security, and building with Okta.&lt;/p&gt;
</description>
        <pubDate>Tue, 20 Jan 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/01/20/xaa-dev-playground</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/01/20/xaa-dev-playground</guid>
      </item>
    
      <item>
        <title>Okta Developer Connect Recap</title>
        <description>&lt;p&gt;Identity has become one of the most important control points in modern systems. As applications grow more distributed and AI-driven automation becomes part of everyday workflows, identity increasingly defines how secure, predictable, and trustworthy those systems are. Decisions about access, scope, and lifecycle now shape not only the user experience, but also how well security holds up as systems scale.&lt;/p&gt;

&lt;p&gt;With this shift in mind, we hosted our first flagship &lt;strong&gt;&lt;a href=&quot;https://regionalevents.okta.com/oktadeveloperconnect&quot;&gt;Okta Developer Connect event&lt;/a&gt;&lt;/strong&gt; in India, in Bengaluru. Led by the Okta Developer Advocacy team, the event brought together developers, architects, engineering managers, IAM practitioners, and technology leaders for a day of focused conversations on how identity needs to evolve as systems scale, and as AI agents begin to act alongside humans.&lt;/p&gt;

&lt;h2 id=&quot;where-identity-security-and-ai-connect&quot;&gt;Where identity, security, and AI connect&lt;/h2&gt;

&lt;p&gt;Throughout the day, the theme stood out clearly: identity now sits at the intersection of application architecture, security decisions, and system behavior. As organizations integrate across ecosystems and introduce AI-driven workflows, identity increasingly determines how safely systems interact and how confidently access can be controlled.&lt;/p&gt;

&lt;p&gt;Identity was discussed as foundational infrastructure, spanning users, applications, APIs, services, and AI agents. The conversations focused on how identity decisions ripple across systems as they become more interconnected, and why those decisions matter long after the first sign-in.&lt;/p&gt;

&lt;p&gt;The sessions balanced identity fundamentals with how teams are applying them today. Core identity standards and practices were discussed as the foundation for building secure, interoperable systems that scale. From there, the conversations expanded into modern use cases across Okta’s platform, including identity for AI-driven systems using the Okta MCP Server, Cross App Access, secure integrations through the Okta Integration Network, automation with Okta Workflows, lifecycle management, and emerging capabilities such as Verifiable Digital Credentials.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/okta-developer-connect-recap/image-1-063315f1150424b260fa798d1e4ebe0d6e781da15af736d3db6e68fbe4b2d234.jpg&quot; alt=&quot;speakers&quot; width=&quot;700&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-shared-conversation-with-the-community&quot;&gt;A shared conversation with the community&lt;/h2&gt;
&lt;p&gt;Beyond the talks, Okta Developer Connect was intentionally designed to be interactive, with open discussions, audience Q&amp;amp;A, quizzes, interactive sessions, surveys, and a research panel created space for participation beyond listening. Attendees engaged directly with Okta engineers, product leaders, and advocates, exchanging perspectives shaped by the systems they build and operate. Those exchanges added important context to the day, grounding the conversations in real systems and practical implementations.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets-jekyll/blog/okta-developer-connect-recap/image-2-0ac56c03de4e4dcbf532f691d9ab6992ca6c26c3470cdf839cf04cbf1e271abf.jpg&quot; alt=&quot;community&quot; width=&quot;700&quot; class=&quot;center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;looking-ahead&quot;&gt;Looking ahead&lt;/h2&gt;
&lt;p&gt;Okta Developer Connect Bengaluru marked the beginning of an ongoing series focused on practical identity education and open technical dialogue. The series aims to help teams think more clearly about building enterprise-ready systems, how identity standards and practices are reflected across the stack, and how AI-driven systems impact access and security assumptions.&lt;/p&gt;

&lt;p&gt;As identity continues to sit at the intersection of security, system architecture, and AI-driven automation, these conversations will only become more important.&lt;/p&gt;

&lt;p&gt;We’re excited to continue building this forum with the developer community, grounded in standards, informed by real-world systems, and focused on designing identity that scales with what comes next.&lt;/p&gt;

&lt;p&gt;Stay tuned for upcoming Okta Developer Connect events, and follow OktaDev on &lt;a href=&quot;https://www.linkedin.com/company/oktadev&quot;&gt;LinkedIn&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/oktadev&quot;&gt;Twitter&lt;/a&gt; for the latest updates.&lt;/p&gt;
</description>
        <pubDate>Mon, 19 Jan 2026 00:00:00 -0500</pubDate>
        <link>https://developer.okta.com/blog/2026/01/19/okta-developer-connect-recap</link>
        <guid isPermaLink="true">https://developer.okta.com/blog/2026/01/19/okta-developer-connect-recap</guid>
      </item>
    
  </channel>
</rss>
