Sam Smith

Case study

Bringing access under one roof

Evolving identity and access management across three generations of an enterprise SaaS console

Lead designer, access-management domain · three generations, ongoing

At a glance

Role
Lead designer for the access-management domain of a multi-tenant SaaS console, across three generations of the platform.
Problem
Each generation of the platform outgrew its access model. The one I inherited could answer "what can this person reach?" only one user at a time, and could never answer "who can reach this?"
What I did
Rebuilt access around resources so it reads in both directions, built a role framework that holds twenty products in one pattern, and carried the lessons into the global console now in design.
Outcome
Three enterprise products adopted it as a shared capability rather than building their own. Auditing access went from one user at a time to a single view. My critique of what shipped became the brief for the next generation.

The setup

The console is where enterprise customers manage what they have bought: the account, the subscriptions, product instances, and the people. I lead design for identity and access management. That means users, groups, service IDs, API keys, and the roles that grant them access.

The story runs across a few years and three generations of the console: the one I inherited, the centralized system I led design for, and the global console currently taking shape. Each generation iterated on the last, bringing more features, and enhancing the deficiencies.

The console I inherited

When I inherited the domain users and groups floated in flat global pools. They existed loosely beside the things they could reach. Answering a question as basic as “what can this person access?” meant finding each user and tracing their roles one resource at a time. The lookup also worked in only one direction: you couldn’t start from a resource and see who could reach it.

That model was only tolerable while the platform was small.

Centralizing access

The trigger

A new version of the console introduced accounts as real boundaries. Accounts are self-contained spaces with their own subscriptions, instances, protected by a unique identity provider. That last part matters, because it means each account carries its own separate pool of users. A model built on one global pool couldn’t express that world.

Turning the model around

The fix was to focus the experience around resources. Users were coming to access management to accomplish a specific goal. The majority of the time that was to add a person to a specific resource.

Instead of browsing pools of users and reverse-engineering what each one could touch, an administrator opens a resource and sees exactly who holds a role on it. The relationship now reads both ways: start from a user to see their resources, or start from a resource to see its users.

The screens turned out to be the easy part. The hard part was one word: resource.

”Resource” meant two different things

Reorganizing around resources only works if our users know what a resource is. Ours were two different things, with two different kinds of access.

Console assets are the account and its subscriptions. The roles on them govern the platform itself: who administers the account, and who manages the plan you pay for.

Product instances are the actual running software. They have their own roles for who can use or administer that software.

One person could administer the account and have no access at all to a running instance. Another could live inside an instance every day with no account rights whatsoever. Users conflated the two constantly, because in their heads it was all just their stuff.

The wall wasn’t where I thought it was

I spent a long time trying to make this legible inside the access-management screens. Nothing worked. Then the research told me why.

Users didn’t understand what the console was for. They had bought a product and expected to go use it. Instead they met a management layer standing between them and their product. My designs were asking them to understand the underlying architecture of the console without context.

So the problem was never “make the word resource clearer.” The problem was that access management was the first place a user met the platform’s architecture, and that was far too late.

Fixing it upstream

The fix belonged in onboarding, where the mental model could be planted long before anyone reached my screens. Onboarding wasn’t my domain, so I drove the change in partnership with the product managers and designers who owned it. We made four moves:

  1. Autoprovisioned setup wherever a product supported it, collapsing the steps between buying and running.
  2. One meaning for “account.” Several systems used the word to mean slightly different things. We settled on one.
  3. A clear line between the product and the console. The main path takes you straight to the product you just bought. The console is framed for what it actually is, the place you manage your subscription, your account, and your users. You can step into it now or come back to it later from the product.
  4. The tour, replaced. A heavy guided tour that most people skipped became dismissible, in-context touchpoints that make their point in the moment.

Access management stopped being where users first met the architecture. They arrived already knowing what a resource was, and “go to a resource and give someone a role on it” finally made sense.

One roof, in practice

The centralized console puts access management on one page. Users, user groups, service IDs, and API keys are each a tab, and a single scope filter controls what every tab shows. The filter is the heart of the design, so rather than describe it, here it is.

The scope filter, drawn lo-fi for publication and running live on this page. Each step carries its UX annotations. Open the flow in its own tab

Role creation, across twenty products

That covered the people managing access. The roles they hand out had to come from somewhere.

More than twenty product teams plug into this console. Their customers needed roles shaped to their own organizations, not just the six fixed roles the console ships with. That meant products needed to expose their roles in the console, where an administrator could tailor and assign them to users.

The catch is that those role models are nowhere near comparable. I needed one pattern consistent enough to feel like a single system, yet flexible enough to hold products that operate in wildly different domains.

First attempt, and why it failed

I underestimated the scale at which some of these products operated. The simple ones keep a tidy list of roles and permissions. Others are products of products, composites carrying dozens of sub-products with roles and permissions of their own.

My first model was a flat list: browse every permission, check the ones you want, and assemble a role. It failed twice over. It didn’t scale, because a flat list drowns under a composite product. It didn’t cohere, because hand-picked permissions don’t necessarily work together, which meant an administrator could build a role that made no sense. Total freedom produced broken roles at a scale nobody could navigate.

What worked

Two decisions fixed it, each answering one half of that failure.

Start from something that already works. Rather than building a role from an empty list, the first step lets an administrator start from scratch or clone an existing role. Cloning pulls in a known-good set of permissions, so you adjust a role that already makes sense instead of assembling one from nothing.

Let the products define the structure. Working with the product teams, we organized permissions into categories that each product owns, and we let the depth vary. For a simple product, the role is the category. For a composite, the categories are whatever that product finds meaningful, things like administrative actions or API calls. However complex the product, the console renders its permissions the same way: by category.

The result is a contract. Product teams define their categories, and the console owns the rendering and the administrator’s experience. The next product to arrive needs no redesign.

Redrawn: a simple product with a flat permission list and a composite of many sub-products, held by the same rendering pattern.

The same care went into what happens after a role exists. Default roles from the console and the products are immutable, so the known-good foundations stay known good. A custom role belongs to the instance it was created for. Its details show every user, group, and service ID that holds it. And deleting one requires typing its name, because a role deletion reaches everyone attached to it.

Underneath all of this, the model supports both role-based and attribute-based access control, presented so that administrators who don’t think in policy models can still use them.

Self-service federation

The other headline of the upgrade was identity-provider setup an administrator can finish alone. Federation is the kind of configuration that used to require manual help from the platform team. Now it is a guided flow that mirrors the handshake itself. One side describes your identity provider to the console. The other describes the console to your identity provider. The form speaks both SAML and OIDC, and it only asks for what the chosen protocol needs.

The risky moments got the most design attention. A new provider starts inactive, so you can configure it without consequences. Activating it is a separate confirmation flow with verification built in, because changing how every user signs in should never be a casual click. And like roles, deleting a provider requires typing its name.

Federation is also how attribute-based access control becomes real, through just-in-time provisioning. Groups your identity provider manages appear in the console as dynamic groups, labeled right beside the ones administrators maintain by hand. Membership stays where your company already manages it. Roles attach to a dynamic group like any other, so access follows your organization’s own structure. The console stays honest about ownership, too. You can’t edit a dynamic group’s membership here, and the add button is disabled rather than hidden, so the boundary explains itself instead of looking like a missing feature.

Measuring a middle layer

A note on metrics, because being straight about them matters more than inflating them.

This console is a middle layer. Its users are both the products that plug into it and the customers who buy those products, so top-line numbers like user growth ride on product sales I don’t control. Claiming them wouldn’t be honest. Here is what is.

  • Three enterprise products adopted this as a shared capability instead of building their own access management. Teams choosing your design over their own budget is the strongest signal I know of.
  • Auditing access went from many steps to one. Answering “what can this person reach?” used to mean opening every user in turn. Now it is a single view.
  • Self-service replaced manual intervention for identity-provider and role management, and the support load behind it went with it.

Broad internal adoption across twenty-plus teams is real, but it was partly mandated, so I treat it as context rather than proof. It was also the actual design challenge: when teams have to use your platform, you still have to earn the partnership. The category framework was built with those teams, not handed to them.

The global console

The centralized console fixed the model, but it made administrators work a filter to see anything. The flow you scrolled through above, resource type, then subscription, then instance, came before every task. Managing a different resource meant clearing the filter and starting the walk again. I had traded one burden for another: nobody traced access user by user anymore, but administrators spent their day driving a filter.

That critique became the brief for the platform’s next generation, where I lead access-management design today. The work is in flight, so what follows is the thinking rather than the screens.

The hierarchy is flattening. The old model nested everything, containers inside containers, and made administrators dig for what they owned. The next generation separates two questions that never belonged together: what you own, and where it runs. What you own surfaces at the top level, in one place, because that is how owners actually think. The words are changing with the shape, too. Where internal systems once used the same terms to mean different things, the next generation borrows the vocabulary administrators already know from the rest of the industry: organizations, environments, instances.

There is no scope picker. You see the resources you actually hold roles on, and selecting one sets the context for everything that follows. Choose a resource or a person once, and every action after that inherits the choice. The view manages itself the same way. Different administrators open the same console and see different, correct interfaces, with nothing to configure.

The research came before the screens. We mapped every administrator role into a small set of personas and sorted their jobs by how often they happen. One finding shaped the architecture more than any other: senior administrators spend most of their time on the most junior tasks, so the hierarchy follows frequency rather than importance. A separate class of jobs must simply never fail, offboarding above all, and those get confirmation, undo, and audit treatment the rest do not.

I killed my own favorite pattern, again. I designed two equal entry points into access, from the resource or from the person, and wrote the model up properly. Then the use-case data showed one mental model was enough. Resource-first won. Killing a pattern you like is still the cheapest simplification there is.

What this project shows

Across three generations, the reported problem was never the real one. “Resource is a confusing word” was an onboarding problem. “Role creation doesn’t scale” was a structure problem. The filter stack I shipped became the brief for the global console. Each fix lived one layer upstream of where the pain showed up, and each generation exposed the blind spot of the one before it. I expect the global console has one too. Finding it will be the next chapter.