Okta Expression Language in Policies
A friendly, ground-up guide to the small formula language Okta uses to make smart decisions. You will learn what an expression is, where expressions appear in and around policies, how data and functions fit together, and how to design, test, and troubleshoot expressions so that your policies do exactly what you meant.
Think about a thermostat. A simple one has a dial: you pick a temperature, and the heating follows it. A smarter one lets you say, “Keep the house at 21 degrees when someone is home, drop to 17 when nobody is, and never let it freeze.” That second thermostat needs more than a dial. It needs a way to describe conditions, combine them, and decide. A tiny formula language sits inside it.
Okta has the same need. Its policies decide how people sign in, what proof they must give, which apps they can open, and what information is passed along. Many of these decisions can be made with simple checkboxes and drop-down lists. But real organizations are messier. They want to say, “If the person is in the Sales department and is not a contractor, and their manager is set, then do this.” To express ideas like that, Okta offers the Okta Expression Language, often shortened to EL.
This guide teaches EL from the beginning. You do not need to be a programmer. We describe expressions in plain words, not in code, so you can focus on the ideas: what data an expression can look at, what it can do with that data, when it is evaluated, and how to keep it understandable. When you are ready to write the exact symbols, you will copy them from the official Okta documentation, which is the only authority on precise syntax. Every new term is explained when it first appears, and each big idea comes with an everyday comparison.
Okta changes its products over time. Where expressions are allowed, which functions and data objects exist, and the exact syntax can differ between the Classic Engine and the Identity Engine, between policy types, and between releases and licenses. This guide deliberately explains ideas in words and avoids code. Always confirm what your own organization supports, and take exact syntax from the official Okta documentation, before you use an expression in production.
1Why Policies Need a Formula Language
Checkboxes can describe simple choices. Real rules need a way to ask questions and combine the answers.
Imagine you manage access for a company with five thousand people. You want finance staff to use a strong proof of identity when opening the payroll system, except when they are in the office on a company laptop. You want contractors to lose access automatically on the date their contract ends. You want the app to receive each person’s department and country in a particular format. None of these wishes is exotic, yet each one needs a decision based on facts about a person.
What fixed settings cannot do
A fixed setting has only a few positions, like a light switch. It cannot say “if this and that, but not the other.” As soon as your wishes need combinations, comparisons, or small calculations, a fixed setting runs out of room. You would need to create many groups and many policies just to cover every combination, and the result would be a pile of near-duplicates that nobody can maintain.
What an expression adds
An expression is a short statement that Okta works out when it needs an answer. It looks at facts, such as the person’s department, their group memberships, or the date, and produces a result, such as yes or no, or a piece of text. Because it is evaluated on demand, it adapts to each person and each moment without you creating a separate setting for each case.
Think of a spreadsheet. You can type fixed numbers into cells, or you can write a formula in a cell that looks at other cells and calculates a result. When the other cells change, the formula updates by itself. Okta Expression Language is like the formula bar for identity: instead of typing a fixed answer, you describe how to work it out.
The cost of power
The same flexibility that makes expressions useful can make them dangerous. An expression that is too clever is hard to read, hard to test, and easy to break with a small change in the data. A good Okta administrator uses expressions where they truly help, and uses simple settings everywhere else.
If a built-in setting can express your idea, use it. Reach for an expression only when the built-in options cannot say what you need. The simplest tool that works is the one the next administrator will understand.
2Core Concepts You Need First
A small set of words appears again and again in this topic. Learn them once and the rest reads easily.
Expression
An expression is a short piece of text that Okta works out to produce a value. Working it out is called evaluating it. The result might be a yes-or-no answer, a number, or a piece of text.
Attribute
An attribute is one fact about something, such as a person’s first name, department, or country. Attributes live in profiles. A user profile holds facts about a person. An application profile holds facts about a person’s account inside an app.
Namespace and object
Expressions need a way to say where a fact comes from. Okta groups data and tools under names, much like labeled drawers in a cabinet. One drawer holds facts about the user. Another holds facts about the device. Another holds helper tools for working with text. These named drawers are often called namespaces or objects.
Operator
An operator is a symbol or word that combines or compares things. Examples in plain terms: “is equal to,” “is not equal to,” “is greater than,” “and,” “or,” and “not.” Operators let you build a bigger question out of smaller ones.
Function
A function is a small ready-made tool that takes some input and gives back a result. For example, a function that checks whether a piece of text contains another piece, or one that joins two pieces of text, or one that tells you the current time. Functions are grouped by topic, such as text, lists, groups, and time.
Boolean
A boolean is a value that is either true or false. Policy conditions and group rules need booleans, because their question is always “does this apply, yes or no?”
Context
Context is the set of facts available at the moment an expression is evaluated. During a sign-in, the context might include the person, the app, the device, and the network. In another moment, such as importing a user, the context is different. An expression can only use facts that exist in its context.
Expression
A short formula Okta evaluates to get a value.
Attribute
One piece of information, such as department or country.
Namespace
A named place where facts or tools live, like user, device, or text tools.
Operator
Compares or combines values, such as equals, and, or not.
Function
A ready-made helper, such as checking text or reading the time.
Context
The facts available at the instant the expression is evaluated.
Think of a recipe card. The ingredients are the attributes. The labeled shelves in the pantry are the namespaces. The cooking steps like “mix” and “chop” are the functions. The words “and” and “or” join steps, like “if the dough is ready and the oven is hot.” The context is whatever is actually in your kitchen today. You cannot use an ingredient you do not have.
3Where Expressions Appear In and Around Policies
Expressions show up in several places in Okta. Some are inside policy rules, and many are in the places that prepare the data policies rely on.
The title of this guide says “in policies,” and it is worth being precise about what that means. Policies make decisions using facts. Those facts come from somewhere: group membership, profile attributes, device information, and more. Expressions can appear in the policies themselves, where Okta supports it, and also in the supporting features that create the facts. A complete understanding needs both.
| Place | What the expression does | How it relates to policies |
|---|---|---|
| Group rules | Decides which people belong to a group automatically | Policies are often assigned by group, so this controls who a policy reaches |
| Policy rule conditions (where custom expressions are supported) | Adds a custom yes-or-no test to a rule | Directly shapes which rule wins |
| Profile attribute mappings | Calculates an attribute value when copying between profiles | Prepares attributes that rules and groups later test |
| Application username format | Builds the username sent to an app | Affects how people appear in the app |
| SAML attribute statements and group filters | Builds the facts passed to an app after sign-in | Affects what the app knows about the person |
| Token claims on authorization servers | Builds claim values in tokens | Affects what APIs learn about the caller |
Why groups matter most
For most organizations, the most common and most important use of expressions near policies is group rules. A group rule says, “Put a person in this group whenever this expression is true for them.” The group is then assigned to policies, apps, or both. In effect, an expression in a group rule is a quiet policy writer: it decides, person by person, who is covered.
flowchart LR
PR["User profile
attributes"] --> GR["Group rule
expression decides membership"]
GR --> GM["Group
membership"]
GM --> PO["Policy or app
assigned to the group"]
PR --> MAP["Attribute mapping
expression builds values"]
MAP --> APP["App profile and
token claims"]
PR --> RC["Policy rule
custom expression, where supported"]
RC --> PO
The diagram shows three paths from the user profile. One goes through a group rule to group membership, and then to policies and apps. One goes through an attribute mapping to the app profile and token claims. A third goes straight into a policy rule, where custom expressions are supported. All three start with the same facts, and all three use the same language.
Think of a school’s office. A clerk uses a formula to sort students into houses based on birth month. Another formula builds each student’s badge text. A third formula decides who gets the early lunch pass. All three are small formulas over the same student records, doing different jobs. EL is the shared formula language across the identity system.
Custom expression conditions inside policy rules are not available for every policy type, engine, or license. Check your Admin Console and documentation to see which rule screens allow them. Where they are not allowed, use groups and group rules to get the same effect.
4The Anatomy of an Expression
Every expression, however long, is built from the same few kinds of pieces.
You can read any expression by splitting it into four kinds of parts. First, references: places where the expression points at a fact, like the person’s department. Second, literals: fixed values written directly, such as the word “Sales” or the number 18. Third, operators: the words or symbols that compare or combine. Fourth, function calls: uses of ready-made tools. A longer expression is just these parts joined together.
Reading an expression in words
Take an idea such as “the person’s department is Sales and their employment type is not Contractor.” In plain words, that has two small questions joined by “and.” The first compares a reference (department) with a literal (Sales) using “is equal to.” The second compares another reference (employment type) with a literal (Contractor) using “is not equal to.” The “and” demands that both be true. That is the whole structure.
flowchart TD
E["Whole expression"] --> A["Left question
department is Sales"]
E --> J["Joiner
and"]
E --> B["Right question
type is not Contractor"]
A --> A1["Reference: department"]
A --> A2["Operator: is equal to"]
A --> A3["Literal: Sales"]
B --> B1["Reference: employment type"]
B --> B2["Operator: is not equal to"]
B --> B3["Literal: Contractor"]
Nesting and grouping
Questions can be placed inside bigger questions, using grouping marks, much like brackets in arithmetic. Brackets tell Okta which parts to work out first. Without them, Okta follows its own order of precedence, which means some operators are applied before others. When in doubt, use brackets. They cost nothing and make your intent clear to both Okta and the next reader.
What an expression returns
Every expression ends up as a single value. In a group rule or a policy condition, that value must be true or false. In an attribute mapping, the value might be text or a number. If an expression returns the wrong kind of value for its job, Okta will reject it or behave unexpectedly. Knowing what each place expects is part of using EL well.
Think of a sentence in plain language: “Open the gate if the visitor has a pass and it is before six o’clock.” It has subjects (visitor, time), tests (has a pass, before six), and a joiner (and). An expression is a sentence like that, written in a strict form that a computer can read without guessing.
Before writing any expression, say it aloud in one plain sentence. If you cannot say it clearly, you are not ready to write it. If the sentence is long, split it into smaller ones.
5Data Types and What Counts as True
Facts come in different shapes. Mixing the shapes is one of the most common sources of surprise.
Every value in EL has a type, which is its shape. Treating a value as the wrong shape leads to wrong answers or errors, so it helps to know the main ones.
Text
Text, also called a string, is a series of characters, such as a name or a department. Text comparisons are usually sensitive to capital letters, so “Sales” and “sales” may not be treated as equal. Text may also hide trailing spaces that you cannot see but that change the result.
Numbers
Numbers are used for counts, levels, and amounts. They can be compared in the usual ways: greater than, less than, equal. A number stored as text, like “42” in quotation marks, is not a number, and comparing it as one can fail or give odd results.
True or false
Booleans are the yes-or-no values. Some profile attributes are boolean, such as “is this account a service account.” Policy conditions must finally produce a boolean.
Lists
Lists, also called arrays or collections, hold several values together. A person might have several group memberships, several phone numbers, or several roles. Functions exist to check whether a list contains a value, how many items it holds, and to combine lists.
Dates and times
Dates and times are often stored as text in a particular format. To compare them, you usually convert them into a form that can be measured, such as a count of days. Time-based rules are powerful but need extra care about time zones and formats.
Nothing at all
Sometimes a fact has no value. The attribute was never filled in, or the person has no manager. This empty state is called null. Dealing with null is so important that it gets its own chapter later.
| Type | Example idea | Common trap |
|---|---|---|
| Text | Department name | Capital letters and hidden spaces change comparisons |
| Number | Employee level | A number saved as text is not treated as a number |
| True or false | Is a contractor | Text “true” is not the same as the value true |
| List | Several roles | Comparing a list with a single value fails |
| Date or time | Contract end date | Formats and time zones |
| Nothing (null) | No manager set | Comparisons on null can error or give surprises |
Think of sorting objects in a toolbox. You cannot measure a color with a ruler or weigh a sound on a scale. Each tool fits one kind of thing. Using a text tool on a number, or a list tool on a single value, is like using a ruler to weigh an apple.
In the profile editor, each attribute is declared as a certain type. That declared type decides how it behaves in expressions. When a rule behaves strangely, check the type of every attribute it touches.
6Referencing Data: The Main Namespaces
Before an expression can decide anything, it must point at facts. Okta organizes those facts into named places.
The exact list of available names depends on where the expression is used and on your version of Okta, so this chapter describes the main kinds in words. When you write the real thing, check the documentation for the names that apply to your situation.
User data
The most used source is the user profile: first name, last name, email, department, manager, country, employee type, and any custom attributes your organization added. Almost every group rule and many mappings start here. There are also facts about the user that are not in the profile, such as status or group memberships.
Application user data
When an expression runs for an app, it can often also reach the person’s account profile in that app. This is useful for building usernames and attributes that follow the app’s own conventions. It lets you combine facts about the person with facts about their app account.
Application and organization data
Some expressions can look at details of the app itself or of your organization, such as the org’s domain name. These help in building values like email-style usernames.
Device, session, and risk context
In policy evaluation contexts, additional objects may be available that describe the device, the session, or the assessed risk of the request. These allow rules such as “when the device is registered” or “when the risk level is high.” Their availability depends heavily on policy type, engine, and license.
Helper tools
Beside data, there are namespaces of tools: text helpers, list helpers, group helpers, time helpers, and conversion helpers. We cover these in the next chapters.
User
Profile attributes plus facts such as status and group membership.
Application user
The person’s account profile inside a particular app.
Application and organization
Details of the app and of your Okta organization.
Device, session, risk
Context objects in policy evaluation, where available.
Text, list, group helpers
Ready-made functions for common jobs.
Time and conversion helpers
Functions for dates and for changing types.
flowchart TB
EX["Expression"] --> U["User facts"]
EX --> AU["App user facts"]
EX --> ORG["App and org facts"]
EX --> CTX["Device, session, risk
where the context has them"]
EX --> TL["Helper tools
text, lists, groups, time"]
Think of an office with filing cabinets labeled People, Accounts, Equipment, and Toolbox. A clerk answering a question opens the right drawers and gathers facts. A clerk working in a back room may only have access to some drawers. The same is true for expressions: what they can reach depends on where they run.
Your organization can add its own attributes to profiles. These show up in expressions just like built-in ones. Name them clearly, and document what each means, because expressions are only as readable as the attributes they point at.
7Operators: Comparing and Combining
Operators are the glue of expressions. A handful of them cover almost everything you will need.
Comparison operators
Comparison operators ask how two values relate. Is one equal to the other? Not equal? Greater? Smaller? Greater or equal? Smaller or equal? These produce a true or false answer. They are the building blocks of nearly every condition. For text, equality usually considers capital letters, so you may need a helper to normalize case before comparing.
Logical operators
Logical operators combine true-or-false answers. “And” demands that both sides be true. “Or” demands that at least one side be true. “Not” flips an answer. These let you build compound questions such as “in Sales and not a contractor,” or “in Finance or in Legal.”
The conditional choice
Many versions of EL offer a compact way to choose between two results depending on a test, sometimes called a ternary or conditional operator. It reads like “if this is true, give me that, otherwise give me the other.” It is useful in attribute mappings, where you want to compute a value, such as using a nickname if one exists and the first name otherwise.
Joining text
A plus sign or a text-joining function is commonly used to glue pieces of text together, as in building a display name from a first name and a last name with a space between. When joining, remember that missing values can turn into odd results.
Order of operations
When several operators appear together, Okta applies them in a defined order of precedence. For example, “and” is typically applied before “or,” just as multiplication comes before addition in arithmetic. Relying on memory of this order is risky. Use brackets to make your meaning explicit.
| Kind | In plain words | Typical use |
|---|---|---|
| Equality | is equal to, is not equal to | Checking a department or a type |
| Ordering | greater than, less than | Levels, counts, dates turned into numbers |
| Logical and | both must be true | Narrowing a rule to a specific audience |
| Logical or | at least one must be true | Widening a rule to several audiences |
| Not | flip the answer | Excluding a group or a type |
| Conditional choice | if this then that else the other | Computing values in mappings |
| Joining text | glue pieces together | Building names and usernames |
flowchart TD
Q["Is the person eligible?"] --> A{"In Sales?"}
A -->|"Yes"| B{"Not a contractor?"}
A -->|"No"| N["Not eligible"]
B -->|"Yes"| Y["Eligible"]
B -->|"No"| N
The diagram shows how “and” behaves: the person must pass the first question and then the second. An “or” would instead let a person pass if either question was answered yes. Drawing a compound expression as a small decision path is a good way to check that you wrote what you meant.
Think of the rules for a library card: “You need a photo ID and a proof of address, or a letter from your school.” The words “and” and “or” decide who gets in. Without brackets, you might read it two ways: does the letter replace both documents, or only the proof of address? Brackets remove the doubt.
Any time you mix “and” with “or” in one expression, add brackets around each group. It is the cheapest insurance against a misunderstanding.
8Functions for Text
Text is the most common kind of data in profiles, so text helpers are the functions you will meet most often.
Okta groups its helper functions into namespaces by topic. The text helpers live in a namespace usually named for strings. The exact list may grow over time, so the documentation is the authority. Here are the common jobs, described in words.
Checking text
Helpers can check whether a piece of text contains another piece, starts with it, or ends with it. They can also check whether the whole text matches a pattern, using a compact pattern language called a regular expression. Regular expressions are powerful but famously hard to read, so use them sparingly and document them well.
Changing text
Helpers can change text to all capitals or all lowercase, trim away spaces at the beginning and end, replace parts, or take a slice from a certain position. Normalizing case and trimming spaces before comparing is a very good habit, because people and systems enter data inconsistently.
Measuring and joining
Helpers can tell you how long a piece of text is, and can join several pieces together, sometimes with a separator between them. Joining is how you assemble usernames, display names, and email-like identifiers.
Extracting parts
A frequent job is pulling a piece out of text, such as the part of an email address before the at sign, or the domain after it. Text helpers can find a position and slice around it. When extracting, always consider what happens if the text does not have the shape you expected.
Contains, starts, ends
Tests whether text includes, begins, or finishes with something.
Case and trim
Lowercases, uppercases, and removes stray spaces before comparing.
Substring and replace
Takes a part of the text or swaps one piece for another.
Concatenate
Glues pieces of text together to build names and identifiers.
Patterns
Tests text against a pattern. Powerful, but hard to read and maintain.
Length
Reports how many characters the text contains.
Think of a mail room clerk sorting envelopes. The clerk checks whether the address contains a certain city, trims the label, converts everything to the same style, and cuts off the apartment number when needed. The clerk’s small, repeatable moves are the text helpers.
Why normalizing helps policies
Data in profiles often comes from many sources: an HR system, a directory, manual typing. One source writes “Sales,” another writes “sales,” a third writes “Sales ” with a trailing space. A group rule that expects exact equality will miss two of the three. A good expression normalizes first, so that all three variants count as the same department.
Better still, fix the source data so that the department is spelled one way. Expressions that clean up messy data are a patch. A clean source is a cure.
9Functions for Lists and Groups
Policies are mostly about groups of people. Group and list helpers let expressions ask group-shaped questions.
Working with lists
List helpers let you ask whether a list contains a certain value, whether it contains any of several values, how many items it holds, and whether it is empty. They also let you combine lists or take a single item from one. Because many profile attributes can hold several values, list helpers are often the right tool when a plain comparison fails.
Group membership helpers
A dedicated set of helpers focuses on groups. In plain words, they answer questions like “is this person a member of the group with this identifier,” “is this person a member of the group with this name,” and “is this person a member of any of these groups.” There are also helpers that gather the groups a person belongs to, sometimes narrowed by a name pattern, which is useful when building group claims for tokens.
Names versus identifiers
Groups have a human-friendly name and a stable identifier. Names can be renamed. Identifiers do not change. An expression that uses a name will break if someone renames the group. An expression that uses the identifier survives renames but is harder to read. Many teams use identifiers for important rules and add a note beside them saying which group each identifier stands for.
Careful with membership timing
Group membership can change by rule or by import, and those changes take time to apply. An expression that asks “is the person in this group” sees the membership as it stands at the moment of evaluation. If a person was just added, the expression may not yet see it. This is another reason to test with real timing.
sequenceDiagram
participant R as Rule or Mapping
participant E as Expression Engine
participant G as Group Data
participant U as User Data
R->>E: Evaluate for this person
E->>U: Read profile facts
E->>G: Ask about group membership
G-->>E: Current memberships
E-->>R: True, false, or computed value
| Question in words | What it needs | Common trap |
|---|---|---|
| Is the person in this group | The group’s identifier or exact name | Renamed groups break name-based checks |
| Is the person in any of these groups | A list of identifiers or names | An empty list matches nobody |
| Which groups does the person belong to | An optional name pattern | Patterns that are too broad return too much |
| How many groups does the person have | The list of groups | Very large groups of memberships slow things down |
Think of a membership club with a printed list of members. Asking “is Priya on the golf list?” is a group question. Using the list’s number is like using the group identifier: it stays the same even if the club renames the list from “Golf” to “Fairway Society.”
Avoid group rules whose expressions depend on membership of the very group they populate, or on other groups that are themselves rule-driven in circles. These loops are hard to reason about and may produce unstable results.
10Functions for Time, Conversion, and More
Besides text and lists, EL offers helpers for the clock, for changing types, and for a few special jobs.
Time helpers
Time helpers can report the current time, convert text to a date, and measure the distance between two dates. These are the ingredients of rules like “a contractor whose end date has passed,” or “an account created in the last thirty days.” Time-based expressions are evaluated when Okta runs them, so the answer can change from day to day for the same person without any profile change.
Conversion helpers
Conversion helpers change one type into another, such as text into a number, a number into text, or a text date into a date value. They solve the common problem where a value looks like the right kind but is stored as the wrong kind. Convert first, then compare.
Country and region helpers
Some helpers translate between country codes and country names, which is useful when different systems use different spellings. They save you from maintaining your own long list of countries.
Directory helpers
When Okta is connected to a directory such as Active Directory, special helpers can reach directory-specific facts. These are used in mappings and are only meaningful when a directory is in use.
Now and date math
Reports the current time and measures the gap between dates.
Conversion
Turns text into numbers or dates and back again.
Country helpers
Translates between country codes and names.
Directory helpers
Reaches facts that exist only in connected directories.
flowchart LR
T["Contract end date
stored as text"] --> C["Convert to a date"]
C --> D["Measure days from today"]
D --> Q{"Is it less than zero?"}
Q -->|"Yes"| X["Contract has ended"]
Q -->|"No"| Y["Contract still active"]
The diagram shows a typical time expression, drawn as a little pipeline. The date starts as text. A conversion helper turns it into a real date. A time helper measures the distance from today. A comparison decides the outcome. Building complex expressions step by step like this makes them easier to design and check.
Think of checking whether a coupon is still valid. You read the printed date, understand it as a date, compare it to today’s date, and decide. If the date is printed in a strange style, you must first translate it. Conversion helpers are the translators.
Be clear about which time zone a date refers to. A contract ending “today” can mean different moments in different countries. When precision matters, store dates in a standard format and document the zone.
11Nothing, Empty, and Messy: Handling Null and Bad Data
Real data is incomplete and inconsistent. Expressions that ignore this fail in the most annoying ways.
Null, empty, and missing
There are three related states. A missing attribute means the profile has no such field. A null value means the field exists but holds nothing. An empty text means the field holds text of zero length. These can look the same on screen but behave differently in expressions. A comparison that expects text may fail or return an unexpected result when given nothing at all.
Guard first, then use
The safest habit is to guard: first check that the value exists, then use it. In words: “if the manager is set, and the manager’s department is Sales.” If the guard is false, the rest is not evaluated in many contexts, so it cannot fail. Without a guard, the expression may crash or return false for the wrong reason.
Defaults
In mappings, you can supply a default for missing values. For example, “use the nickname if there is one, otherwise the first name.” This keeps the app from receiving an empty field. Defaults should be sensible and visible, not made-up values that hide a data problem.
Case and spaces
As discussed, capitals and trailing spaces cause subtle mismatches. Normalize before comparing. Also be aware of unusual characters copied from documents, such as non-breaking spaces or fancy quotation marks, which look normal but do not match.
Multi-valued confusion
If an attribute can hold a list, a single value comparison will not behave as you expect. Check the declared type and use list helpers where needed.
| Situation | Risk | Safe habit |
|---|---|---|
| Attribute never filled in | Errors or false answers | Guard with an existence check |
| Different capitalization | Rule misses people | Normalize case before comparing |
| Trailing spaces | Equality fails silently | Trim before comparing |
| Number stored as text | Wrong comparisons | Convert before comparing |
| Attribute holds a list | Single-value test fails | Use list helpers |
| Odd copied characters | Invisible mismatch | Clean the source data |
Think of a form at a doctor’s office. Some people leave a field blank, some write “N/A,” some write in the wrong box. A careful receptionist checks each answer before using it. An expression that assumes every form is filled in perfectly will make mistakes on the very first messy form.
When an expression fails to evaluate, the result is often that the condition is not met. For a rule that grants easy access, that is safe. For a rule that is meant to block, it might silently let people through. Decide which direction is safe for each rule, and test the failure case on purpose.
12Group Rules: Expressions That Decide Who a Policy Reaches
For most organizations, the single most valuable use of EL near policies is the group rule.
A group rule is an automatic membership instruction. It says, “Whenever a person’s profile satisfies this expression, place them in that group; whenever it no longer does, remove them.” Because policies and app assignments are very often tied to groups, a group rule is an indirect way to say, “this policy applies to people who satisfy this expression.”
Why dynamic groups are powerful
Without group rules, administrators add and remove people by hand. People change roles, leave, or join, and the groups slowly drift from the truth. With group rules, the group follows the data. When HR updates a person’s department, the person moves to the right group, and with it to the right policy. This keeps access aligned with reality and reduces manual work.
What group rule expressions look like in words
A typical group rule expression compares one or more profile attributes with fixed values: department equals Sales, country is one of a list, employee type is not Contractor. They are intentionally simple. Group rules are meant to be easy to read, because they quietly affect many people. Okta also validates them when you save and may limit their length and complexity.
The timing of group rules
Group rules are processed by Okta in the background. After you create or change one, it is activated, and then the membership is recalculated. A change in a person’s profile also triggers re-evaluation. These processes take some time, so membership may lag behind. Plan around that delay in your testing and your communications.
Excluding people
Group rules can also name people to exclude, who never join the group even if the expression is true. Use exclusions sparingly, and document why each person is excluded, since an exclusion is an exception that others cannot see in the expression itself.
flowchart TD
CH["Profile changes
for example department"] --> EV["Group rule evaluates
the expression"]
EV --> T{"True for this person?"}
T -->|"Yes"| ADD["Person joins the group"]
T -->|"No"| REM["Person leaves the group"]
ADD --> POL["Policies and apps tied
to the group now apply"]
REM --> POL2["Policies and apps tied
to the group stop applying"]
Think of a school that sorts students into clubs automatically: everyone born in spring joins the Garden Club. When a student’s records are corrected, the student moves automatically. No teacher needs to remember to update the list. A group rule works like that automatic sorting.
Treat group rules as policy. Give each rule a clear name, a description of its purpose, and an owner. A group rule that nobody understands is as risky as a policy that nobody understands.
13Expressions Inside Policy Rule Conditions
Where Okta allows it, you can place a custom expression directly into a policy rule as one of its conditions.
Policy rules normally use built-in conditions: group, network zone, device state, risk level, and so on. These are quick to set and easy to read. Sometimes they cannot express what you need. Some policy screens therefore offer a place to add a custom expression as an extra condition. The expression must produce a true or false answer. When it is true, along with all the other conditions, the rule applies.
When a custom expression helps
Custom expressions are most useful for conditions the built-in options do not cover. Examples in words: “the person’s profile says they are a manager and their cost center begins with a certain code,” or “the device’s operating system version is above a certain number and the person is not in a given group,” or “the request arrives on a weekday.” They let one rule capture a combination that would otherwise need extra groups and extra rules.
How it fits into rule evaluation
A custom expression is just another condition in the rule. All of a rule’s conditions must be true together. So a custom expression narrows the rule, never widens it. The rule’s position in the list still decides when it is read. First match still wins. An expression does not change the ordering logic, it only changes whether the rule matches.
flowchart TD
R["Rule is read in order"] --> C1{"Built-in conditions true?
group, zone, device"}
C1 -->|"No"| NEXT["Skip to the next rule"]
C1 -->|"Yes"| C2{"Custom expression true?"}
C2 -->|"No"| NEXT
C2 -->|"Yes"| ACT["Rule wins
apply its action"]
What can go wrong
Custom expressions add power and risk in equal measure. They can reference data that is not available at the moment of evaluation. They can hide complexity inside a single field that few people will inspect. And they are easy to get subtly wrong, such as a wrong capital letter or a missing guard for empty values. A rule with a broken expression may never match, or may match when it should not.
| Need | Better tool first | Custom expression when |
|---|---|---|
| Target a department | A group, filled by a group rule | Never needed for a simple department |
| Target a location | A network zone | The location logic is truly unusual |
| Require a managed device | The built-in device condition | You need a combination the screen cannot express |
| Time-based access | Built-in options if available | The rule depends on dates or days of the week |
| Complex combination | Several simple rules in order | One rule must hold the whole combination |
Think of a door with a standard lock and a special extra latch you can program yourself. The standard lock covers nearly every case. The custom latch is for a unique situation. If you rely on the custom latch for everything, nobody else knows how to open the door.
Not every policy rule screen accepts custom expressions, and the rules for what they may reference are specific. Look for a custom-expression option on your rule screen and read the documentation for that policy type before relying on it.
14Evaluation Context: What Is Available When
An expression can only use facts that exist at the moment it runs. This is the most common hidden reason expressions fail.
Okta evaluates expressions at many different moments: when a group rule recalculates, when a person is imported, when a person signs in, when an app is opened, when a token is created. At each moment, a different set of facts is available. Think of each moment as a different room with different furniture.
Examples of differing context
During a group rule calculation, the user profile is available, but there is no device and no network, because nobody is signing in. During a sign-in policy evaluation, device and network facts may be available, as well as the user. During a token creation, facts about the app and the requested access may be available. During an attribute mapping at import, the source profile may be available, but not any live session.
The consequence
An expression written for one room may point at furniture that does not exist in another. If you copy a device-based expression into a group rule, it will fail, because group rules run without a device. The same applies the other way round: not every profile detail is available to every policy screen.
| Moment | Typically available | Typically not available |
|---|---|---|
| Group rule recalculation | User profile, status | Device, network, live session |
| Attribute mapping at import or update | Source and target profiles | Device, risk, live session |
| Policy evaluation at sign-in | User, groups, and context such as device, network, and risk where supported | Facts that depend on a future step |
| Token creation | User, client app, requested scopes | Anything outside token context |
flowchart LR
subgraph G["Group rule room"]
G1["User profile"]
end
subgraph S["Sign-in policy room"]
S1["User profile"]
S2["Groups"]
S3["Device and network"]
S4["Risk"]
end
subgraph T["Token room"]
T1["User profile"]
T2["Client app"]
T3["Scopes"]
end
The diagram shows three rooms with different contents. The profile appears in all three, which is why profile-based expressions are the most portable. Device and risk appear only in the sign-in room, which is why expressions using them cannot be used elsewhere.
Think of three kitchens: a home kitchen, a food truck, and a hotel buffet. A recipe that calls for an oven works in the first and last, but not in the food truck without one. The recipe is fine. It was just written for a different kitchen. Expressions must be written for the room they will run in.
Where possible, base expressions on profile attributes, which are available almost everywhere. Use device or risk facts only in the policy screens designed to provide them.
15How Okta Evaluates an Expression
Understanding the evaluation steps helps you predict results and find mistakes.
When Okta meets an expression, it does a short series of steps. First it reads the text and checks that it is well formed, like checking that a sentence has proper grammar. This step is called parsing. If the grammar is wrong, Okta refuses it, often right when you save. Second, it gathers the facts the expression references from the current context. Third, it works out the pieces from the inside outward, calling functions, applying operators, and respecting brackets. Fourth, it checks that the final value has the shape the place expects, such as a boolean for a condition. Finally, it uses the result.
sequenceDiagram
participant A as Admin
participant P as Parser
participant C as Context
participant V as Evaluator
participant U as Feature using it
A->>P: Save the expression
P->>P: Check grammar and known names
P-->>A: Accepted or rejected
U->>V: Need an answer for this person
V->>C: Fetch referenced facts
C-->>V: Values or nothing
V->>V: Work out pieces inside out
V-->>U: Final value
Save time versus run time
The diagram separates two moments. At save time, Okta checks the grammar and that names exist. A passing save does not prove that the expression behaves correctly. At run time, Okta uses real data, which may be missing, oddly formatted, or of an unexpected type. Many errors only show up then. This is why saving successfully is only the first step of testing.
Short-circuiting
For “and” and “or,” Okta often stops as soon as the answer is certain. If the first part of an “and” is false, the whole thing is false, so the second part may never be worked out. This is why guards work: put the safety check first, and the risky part is skipped when the check fails. Do not rely on short-circuiting for side effects, since expressions should be calculations without lasting side effects.
Evaluation is repeated
Expressions are not stored as answers. They are worked out again each time they are needed, with the facts of that moment. This is what makes them adaptive. It also means a change in the data can change the result without anyone editing the expression.
Think of a teacher grading a quiz. First the teacher checks that the answer sheet is filled in properly. Then the teacher compares each answer to the key, from the first question to the last. The grade is calculated each time a new sheet arrives. The key does not store anyone’s grade in advance.
An expression Okta accepts is grammatically correct. It may still be logically wrong. Always test with real examples of people who should and should not match.
16Attribute Mappings: Building the Facts Policies Use
Many policy problems start long before the policy, in how the underlying attributes were prepared.
Profiles in Okta can come from many sources: a directory, an HR system, an application, manual entry. Attribute mappings define how values are copied from one profile to another. A mapping can be a straight copy, or it can be an expression that calculates the value. The result is the attribute that group rules, policies, and apps later rely on.
Typical mapping jobs
Common jobs include building a display name from first and last name, building a username in the form an app expects, turning a department code into a friendly department name, choosing between a nickname and a first name, and converting country names into standard codes. Each job is a small expression over the source profile.
Mapping direction
Mappings have a direction. Some copy from an outside source into the Okta profile. Others copy from the Okta profile to an app profile. The direction decides which facts are available and when the mapping runs. Mappings can also be set to apply only on creation, or on every update, which changes how often your expression is worked out.
Why this matters for policies
If the department attribute is wrong, the group rule that depends on it is wrong, and the policy assigned to that group is wrong. A policy cannot be better than the data that feeds it. Spending effort on clean, consistent attributes, prepared by simple mapping expressions, is often the most effective way to improve policy accuracy.
flowchart LR
SRC["Source system
HR or directory"] --> MAP1["Inbound mapping
expression cleans values"]
MAP1 --> PROF["Okta user profile"]
PROF --> GR["Group rules and policies
test the clean values"]
PROF --> MAP2["Outbound mapping
expression builds app values"]
MAP2 --> APP["Application profile"]
Think of a kitchen’s prep area. Vegetables are washed and chopped before they reach the cooks. The cooks follow recipes that assume clean, evenly cut vegetables. If the prep is sloppy, even the best recipe produces a poor meal. Mappings are the prep work for policies.
Use the preview tools in the profile editor, where available, to see what a mapping produces for a real person before you save it. Check a handful of people with unusual data.
17Token Claims and Application Statements
After a person signs in, expressions also decide what facts are passed to the app.
Claims in tokens
A claim is a statement about a person carried inside a token, such as their email or group list. On authorization servers, administrators can define custom claims whose values come from expressions. The expression is worked out when the token is created. A claim might hold the person’s department, a list of their group names matching a pattern, or a computed role label.
Statements for SAML apps
For apps that sign in using SAML, administrators define attribute statements and group attribute statements. Values can come from expressions over the user profile. The app then receives these facts and may use them to decide what the person sees or can do.
Policy meets claims
Access policies on an authorization server decide whether a token is issued at all. Claim expressions decide what goes inside it. Together they shape the app’s view of the person. If an app grants powers based on a role claim, then the expression that produces that claim is as sensitive as any access policy.
Keep claims small and necessary
Every claim adds to token size and exposes information. Include only what the app needs. A group claim that lists hundreds of groups can make tokens too large to handle. Use filters, such as matching only groups with a certain naming pattern, to keep it focused.
Custom claims
Expressions produce values that travel inside tokens to APIs.
Attribute statements
Expressions build the facts sent to SAML apps after sign-in.
Group filters
Patterns decide which group names appear in a claim or statement.
Keep it small
Only send what the app truly needs to avoid oversized tokens and oversharing.
Think of a conference badge. The badge shows your name and your access level, not your home address or your salary. What is printed on the badge is a deliberate decision. Claims are the printing on the digital badge, and expressions decide what gets printed.
Do not put sensitive personal data into tokens or statements unless the app truly needs it and it is protected in transit. Anything in a token may be visible to the app and to anyone who can read the token.
18Architecture and Components
Now we zoom out to see how the pieces involved in expressions fit together.
Expressions are not a separate product. They are a shared capability that many Okta features call on. Understanding the parts helps you reason about where a problem lives.
flowchart TB
subgraph Authoring["Where expressions are written"]
GRU["Group rules"]
MAPS["Attribute mappings"]
POLR["Policy rule conditions"]
CLM["Claims and statements"]
end
subgraph Core["Expression capability"]
PARSE["Parser
checks grammar"]
EVAL["Evaluator
works out values"]
LIB["Function library
text, lists, groups, time"]
end
subgraph Data["Facts"]
PROF["User and app profiles"]
GRP["Group memberships"]
CTX["Device, session, risk context"]
end
GRU --> PARSE
MAPS --> PARSE
POLR --> PARSE
CLM --> PARSE
PARSE --> EVAL
EVAL --> LIB
PROF --> EVAL
GRP --> EVAL
CTX --> EVAL
EVAL --> OUT["Result: true or false,
or a computed value"]
OUT --> LOG["System Log and previews"]
The parts, one by one
Features that accept expressions
Group rules, mappings, some policy rules, and claims each let you write expressions.
Parser
Confirms the expression is well formed and uses known names.
Evaluator
Works out the value using the facts of the moment.
Function library
The text, list, group, time, and conversion helpers.
Data sources
Profiles, group memberships, and the context of the current moment.
Logs and previews
Where you can see what happened or what would happen.
Because the parser, evaluator, and function library are shared, learning EL once pays off in every feature. What differs from feature to feature is which facts are available and what kind of result is expected. When something breaks, ask which feature the expression lives in, what facts it had, and what kind of result the feature wanted.
Think of a calculator built into several apps on your phone: the budget app, the recipe app, the fitness app. The calculator works the same way everywhere, but each app feeds it different numbers and wants a different kind of answer. EL is that shared calculator.
19Worked Examples in Plain Words
The best way to learn is to walk through real situations. Each example below is described in words, not code, and followed through step by step.
Example 1: A group for full-time sales staff
The goal: place every full-time person in the Sales department into a group called “Sales Full-Time,” so that a stricter sign-in policy for customer data applies to them. In plain words, the group rule’s expression says: the department is Sales, and the employment type is Full-Time. Two small questions joined by “and.”
Now consider three people. Rohan is in Sales and Full-Time. Both questions are true, so he joins the group. Sneha is in Sales but a contractor. The second question is false, so she does not join. Imran is in Sales and Full-Time, but his department was typed as “sales” in lowercase by mistake. The first question compares exact text, so it is false, and Imran is missed. The fix is either to clean his data at the source or to make the expression normalize case before comparing.
Example 2: Contractors whose contract has ended
The goal: automatically move contractors out of an access group when their contract end date has passed. In words: the employment type is Contractor, and the end date is set, and the end date is earlier than today. The guard, “the end date is set,” comes before the date comparison, so that contractors with no end date do not cause errors.
Consider two contractors. Leela has an end date last week. All three parts are true, so the rule applies. Mark has no end date recorded. The guard fails and the rule does not apply. Whether Mark should lose access or keep it is a policy decision, not a technical one. The example shows why you decide, in advance, how missing data should be treated.
Example 3: Building a username for an app
The goal: send an app a username made from the person’s first name, a dot, and the last name, all in lowercase. In words: take the first name, make it lowercase, add a dot, add the last name made lowercase. This is a mapping expression, not a condition, so it returns text.
For “Asha Rao,” the result is the lowercase first name, a dot, and the lowercase last name. For a person whose last name contains a space, or an accent, the result may not suit the app. A careful designer checks several real names, including awkward ones, before saving.
Example 4: A custom condition in a sign-in rule
The goal: in a rule for a sensitive app, apply a stricter requirement when the person is a manager and the sign-in is not from the office network. If the policy screen supports custom expressions, the “manager” part can be written as a profile test, while the office network part uses the built-in network zone condition. Using both together keeps the expression small and the rule readable.
| Example | Where it runs | Returns | Main lesson |
|---|---|---|---|
| Full-time sales group | Group rule | True or false | Normalize text before comparing |
| Contractor expiry | Group rule | True or false | Guard against missing dates |
| Username for an app | Attribute mapping | Text | Test with unusual real names |
| Manager off-network | Policy rule condition | True or false | Combine simple built-ins with a small expression |
Think of writing the entry rules for a community pool: “Residents over twelve with a valid pass, except on cleaning days.” Each phrase is a condition. Testing it with a few real visitors, including a thirteen-year-old with an expired pass, reveals gaps that the plain sentence hid.
For every expression, write down at least three test people: one who should match, one who should not, and one with messy or missing data. Trace each through the expression in words before you save.
20Designing Expressions That Stay Readable
An expression that works today but nobody can read tomorrow is a liability.
Keep each expression small
A short expression is easier to read, test, and fix. If you find yourself writing a long chain of conditions, ask whether part of it could become its own group, its own attribute, or its own rule. Splitting a big idea into two or three small pieces is nearly always better than one clever expression.
Prefer clean attributes to clever expressions
If you often write the same awkward expression to cope with messy data, consider adding a clean attribute that prepares the data once. An attribute called “Access tier” with values like High and Standard is far easier to use in many rules than repeating a complicated test everywhere.
Name and describe
Give every group rule, mapping, and policy rule a name that describes its purpose, not its mechanics. Add a plain-language description saying who it is for, why it exists, and who owns it. In the description, write the expression’s meaning in one sentence. The expression says how. The description says why.
Make assumptions visible
Every expression assumes something: that a value is filled in, that text is lowercase, that a group has a certain identifier. Write those assumptions down. When the data changes, the assumption is the first place to look.
Use identifiers for stability, with notes
For groups and similar objects, use identifiers where stability matters, and keep a short note of what each identifier means. For readability, names are friendlier but fragile.
Small and single-purpose
One idea per expression, so it can be read in one breath.
Clean attributes
Prepare the facts once instead of repeating clever fixes.
Names and descriptions
Say what it is for, who owns it, and what it means in one sentence.
Written assumptions
Note what the expression expects from the data.
Identifiers with notes
Survive renames and explain what each identifier is.
Second pair of eyes
Ask a colleague to read the description and the expression together.
flowchart TD
BIG["One long clever expression"] --> SPLIT{"Can it be split?"}
SPLIT -->|"Yes"| P1["Small expression A
prepare an attribute"]
SPLIT -->|"Yes"| P2["Small expression B
group rule"]
SPLIT -->|"Yes"| P3["Simple policy rule
uses the group"]
SPLIT -->|"No"| KEEP["Keep it, but document it well"]
Think of a road map with clear labels versus a hand-drawn shortcut known only to the person who drew it. The shortcut may be quicker for them, but anyone else will get lost. Readable expressions are the clearly labeled map.
21Security Considerations
Expressions decide who is covered by security rules. Mistakes in them are security mistakes.
Expressions as hidden access control
A group rule that puts people into an administrators group is an access control, even though it looks like a small formula. Anyone who can edit that expression can effectively grant admin rights. Treat the right to create or edit group rules and custom conditions as a privileged capability, and limit it to a small trusted set of administrators.
Trusting the data behind the expression
An expression is only as trustworthy as the attribute it tests. If people can edit their own profile attributes, and an expression grants access based on one of them, then a person might promote themselves by changing their own department. Make sure the attributes used for security decisions can only be changed by trusted sources, such as the HR system, and not by the person themselves.
Failure direction
Think about what happens when the expression cannot be evaluated. For an allow rule, failure usually means no match, which is safe. For a rule meant to block or tighten, failure may mean that the stricter rule does not apply and people slip through. Where possible, build blocking behavior so that failure is safe, for example by making the catch-all rule restrictive.
Least privilege in group rules
Write group rules as narrowly as possible. A rule that says “department contains Eng” may unexpectedly match “Engagement,” “Engineering,” and “Genealogy.” Exact matches and well-defined lists are safer than loose pattern matching.
Information in claims
Tokens and statements travel to other systems. Limit the facts you place in them. Avoid including sensitive personal data. Review claims whenever an app’s needs change, so that old, unnecessary claims do not linger.
Change control and audit
Require a review for changes to expressions that affect access, and keep a change record. The System Log keeps a trail of administrative changes, and your own notes should explain the reason for each. This is what you will lean on during an audit.
| Risk | Example | Safeguard |
|---|---|---|
| Self-promotion | Person edits own department to join a privileged group | Make security-relevant attributes read-only for users |
| Loose matching | Pattern matches more departments than intended | Use exact matches or defined lists |
| Silent failure | Blocking rule never matches due to bad data | Restrictive catch-all and tests of the failure case |
| Overpowered editors | Many admins can edit group rules | Limit roles and require review |
| Oversharing in tokens | Claim contains sensitive data | Send only what the app needs |
Think of a building where doors open for anyone wearing a blue badge, and anyone can print their own badge. The lock is strong, but the rule makes it meaningless. The security of an expression depends on how hard it is to fake the facts it tests.
For every attribute used in a security expression, ask who can change it and how. If the answer includes the person themselves or an unreviewed process, the expression is not a reliable gate.
22Testing and Validation Before You Publish
A successful save proves only that the grammar is fine. Testing proves that the logic is.
Use the tools Okta provides
Okta offers helpful checks in several places. When you save a group rule or a mapping, the system validates it and reports errors in grammar or unknown names. In the profile editor, a preview can show what a mapping would produce for a chosen person. Some policy screens offer simulation or preview tools. The availability of these tools varies, so look for them in your console.
Test with real people, on purpose
Choose a handful of real or realistic test profiles: one who should match, one who should not, one with missing data, one with unusual capitalization, and one who belongs to many groups. Walk each through the expression and write down the expected outcome before you test. Then compare.
Test in a safe place
Use a preview or test organization if you have one. Create test users with the edge-case data. Try the group rule or mapping there, and see who ends up where. Only then repeat the change in production.
Stage the rollout
For group rules, there is a useful safe path. Create the rule, check what it would do before activating it, and then activate it. After activation, look at the group’s membership and compare it to your expectation. Check both the count and a sample of names.
Say it in a sentence
Write the intent in plain words, including how missing data should be treated.
Write it small
Build the expression from small pieces, checking each piece alone.
Validate
Save to check grammar, then use previews or simulation where available.
Test the five people
Should match, should not, missing data, odd formatting, many groups.
Try it in a test organization
See the real effect without risk to users.
Activate and verify
Switch on, then check membership counts and sample names.
Record
Write down the intent, the test results, and the owner.
Think of a chef tasting a sauce before serving it. The recipe may look correct on paper, but tasting catches a missing pinch of salt. Testing an expression with sample people is tasting the sauce.
Maintain a small set of permanent test users with tricky data. Reuse them every time you change an expression, so that tests become quick and repeatable.
23Monitoring, Logging, and Debugging Expressions
When an expression misbehaves, you need to see what it saw and what it returned.
Where to look
Several places can tell you the story. The System Log records administrative changes, such as who edited a group rule and when, and many events about sign-ins and policy decisions. The group itself shows its membership and, in some views, how each person came to be a member. The profile editor preview shows what a mapping would compute. The user’s profile page shows the actual attribute values the expression saw.
A debugging routine
Start with the person, not the expression. Look at the person’s actual attribute values, including capitalization and empty fields. Then read the expression in words and trace it using those values. Most surprises come from data that is not what you assumed. Next, check the group or policy result: is the person in the group, did the rule apply? Finally, check timing: has enough time passed for background processing, and does a live session explain the delay?
Watching for drift
Over time, groups driven by expressions can drift as data changes. Watch group sizes. A sudden jump or drop is a signal. If the Sales group doubles overnight, something changed in the data or in the rule. Set up simple reports that list group counts and review them regularly.
flowchart TD
S["Surprising result"] --> A["Look at the person's actual attribute values"]
A --> B["Trace the expression in words with those values"]
B --> C{"Does the trace match the result?"}
C -->|"Yes"| D["Fix the data or the intent"]
C -->|"No"| E["Check context, timing, and other rules"]
E --> F["Check System Log and group membership"]
Think of an accountant checking a total that looks wrong. They do not start by rewriting the formula. They check the numbers that went in, redo the sums by hand, and compare. Only if the sums disagree with the formula do they suspect the formula itself.
The majority of expression problems are data problems. Begin by looking at what is really stored for the person.
24Advantages, Disadvantages, and Trade-offs
Expressions are a sharp tool. Use them where they cut well and avoid them where they do not.
Advantages
- Express combinations that checkboxes cannot.
- Adapt automatically to each person and moment.
- Keep groups and attributes in step with the data.
- Reduce the number of near-duplicate groups and policies.
- Shared language across groups, mappings, and claims.
- Lower manual workload for administrators.
Disadvantages
- Harder to read than simple settings.
- Mistakes can be silent and widespread.
- Depend on data quality and attribute types.
- Availability varies by policy type, engine, and license.
- Context differences make expressions hard to reuse.
- Knowledge can sit with one administrator.
Choosing the right tool
For any need, work down a ladder from simplest to most powerful. First try a built-in condition. Then try a group, perhaps filled by a simple group rule. Then try a small expression in a mapping to prepare a clean attribute. Only then consider a custom expression in a policy rule. At each step, the cost of understanding and maintaining grows.
| Approach | Readability | Power | Risk |
|---|---|---|---|
| Built-in condition | Very high | Limited | Low |
| Manual group | High | Limited | Drifts with human error |
| Group rule with simple expression | High | Moderate | Depends on data quality |
| Mapping that prepares a clean attribute | Medium | High | Medium |
| Custom expression in a policy rule | Lower | Highest | Highest, needs review and tests |
flowchart TD
N["A new access need"] --> Q1{"Can a built-in condition do it?"}
Q1 -->|"Yes"| U1["Use the built-in condition"]
Q1 -->|"No"| Q2{"Can a group express it?"}
Q2 -->|"Yes"| U2["Use a group, with a simple group rule if needed"]
Q2 -->|"No"| Q3{"Can a clean attribute express it?"}
Q3 -->|"Yes"| U3["Prepare the attribute with a mapping"]
Q3 -->|"No"| U4["Use a custom expression with tests and review"]
Think of tools in a workshop. A screwdriver handles most jobs. A power drill handles harder ones. A specialist router is for the rare job nothing else can do. You do not reach for the router first, because it is the easiest to misuse.
25Design Patterns for Expressions
A pattern is a proven way of solving a problem that keeps coming back. These patterns help you use expressions with confidence.
Pattern: Guard, then act
Before using a value, first check that it exists. In words: “if the end date is set, and it is earlier than today.” The guard comes first so that the second part never meets an empty value. Because Okta stops reading a chain of “and” tests once one of them is false, the guard protects the part that follows it.
Pattern: Normalize, then compare
Text can be written many ways: capital letters, small letters, extra spaces. Before comparing, make both sides look the same. For example, trim the spaces and make the text lowercase, then compare. This pattern removes a whole family of silent mismatches.
Pattern: Prepare once, use everywhere
If several rules need the same awkward calculation, do it once. Create a clean attribute, perhaps “Access tier,” filled by a mapping. Then every group rule and policy can simply test that attribute. Fixes happen in one place instead of ten.
Pattern: Group as the bridge
Use a group rule to turn profile facts into group membership, and let policies reference the group. The group is the readable name for an idea. Policy rules then say “people in the Finance Approvers group,” which anyone can understand.
Pattern: Explicit default
When an expression produces a value, decide what the fallback is. If the department is missing, should the person land in a “Needs review” bucket? Choosing a deliberate fallback is much better than leaving the outcome to chance.
Pattern: Lists over long chains
If you need “department is Sales or Support or Marketing or Success,” consider a list-based test, such as “department is one of this list.” A list is shorter, and adding a new item means adding one word instead of another clause.
Guard, then act
Check a value exists before using it.
Normalize, then compare
Make both sides look the same first.
Prepare once
Build a clean attribute and reuse it.
Group as the bridge
Let a named group carry the idea.
Explicit default
Choose the fallback on purpose.
Lists over chains
Shorter and easier to extend.
flowchart LR
DATA["Messy profile data"] --> NORM["Normalize
trim and lowercase"]
NORM --> GUARD{"Value present?"}
GUARD -->|"Yes"| TEST["Compare or test"]
GUARD -->|"No"| FALL["Explicit fallback"]
TEST --> GRP["Group membership"]
FALL --> GRP
GRP --> POL["Policy rule uses the group"]
Think of a kitchen. Good cooks wash and chop ingredients first, check that they have everything, and prepare a base sauce once for many dishes. These habits are the cooking equivalents of normalize, guard, and prepare once.
26Anti-patterns to Avoid
An anti-pattern is a habit that looks sensible but causes trouble later. Each card below names one, shows how it appears, and how to fix it.
What it looks like
One enormous expression with many nested conditions that only its author understands.
Why it hurts
Nobody can safely change it. Bugs hide in the middle and tests are hard to design.
Better approach
Split it into a clean attribute, a group, and a short policy condition.
What it looks like
An expression that uses a value without checking if it is present.
Why it hurts
People with blank data cause errors or silently fall out of rules.
Better approach
Guard first, and decide what blank data should mean.
What it looks like
Granting access based on an attribute that users can change themselves.
Why it hurts
People can give themselves access by editing their own profile.
Better approach
Base security decisions on attributes owned by a trusted source, and make them read-only for users.
What it looks like
Using partial text matching where an exact match was intended.
Why it hurts
The rule catches people it was never meant for, such as similarly named departments.
Better approach
Use exact matches or a defined list of allowed values.
What it looks like
A rule named “Rule 7” with no description, no owner, and no stated purpose.
Why it hurts
Nobody dares remove it, nobody knows if it still works, and audits become painful.
Better approach
Name by purpose, describe in one sentence, and record the owner and review date.
What it looks like
Pasting an expression from one place into another where the available objects differ.
Why it hurts
The same words may mean something else, or be unavailable, in the new place.
Better approach
Check that every name in the expression exists in the new context, and test again.
27Best Practices and Common Mistakes
This chapter gathers the habits that separate calm administrators from frantic ones.
Best practices
Start from the sentence
Write the rule in everyday language first, including how blanks are handled.
Use the simplest tool
Prefer built-in conditions and groups over custom expressions.
Clean the data at the source
Fix the source system so expressions need fewer workarounds.
Normalize and guard
Standardize text and check presence before every comparison.
Test with five people
Should match, should not, blank data, odd formatting, many groups.
Describe and own
Every expression has a name, a description, and an owner.
Review on a schedule
Revisit rules every quarter and retire the ones no longer needed.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
| Forgetting case differences | People are missed by exact comparisons | Normalize before comparing |
| Ignoring blanks | Errors or silent non-matches | Guard and set a fallback |
| Reusing across contexts | Names missing or meaning different | Re-check objects and retest |
| Trusting self-edited data | People promote themselves | Use trusted, read-only sources |
| Too clever, too long | Nobody can maintain it | Split into small parts |
| No testing after saving | Valid grammar, wrong logic | Trace sample people first |
| Expecting instant effect | Confusion about live sessions and delays | Test with a fresh sign-in and allow time |
| No ownership | Orphan rules stay forever | Record owner and review dates |
Think of a pilot’s checklist. Experienced pilots still use one, not because they forget how to fly, but because small slips matter. These best practices are your checklist for expressions.
Before saving any expression, ask three things: what do blanks do, who can change the data, and who will understand this in a year.
28Real-World Scenarios
These short stories use made-up companies to show how the ideas appear in daily work. They are teaching examples, not descriptions of any real organization.
Scenario 1: The group that doubled overnight
Larkstone Health noticed that a group for “clinical staff” had suddenly doubled. The team looked at the new members and saw that a data feed had started writing a department name with a different spelling, and the group rule used a loose text match that accepted it. They changed the rule to an exact list of approved departments and asked the HR system owner to standardize the feed. Group size reports now alert them to sudden jumps.
Scenario 2: The missing end dates
Westmark Logistics used a rule to remove contractors from a warehouse group after their contract end date. During an audit, they found that contractors with no end date never left the group. The original expression compared dates without checking whether a date existed. They added a guard, created a “Needs review” group for contractors without dates, and gave the vendor manager a monthly list to clean up.
Scenario 3: The username surprise
Pinecrest Finance sent usernames to a partner app built from first and last names. When a new employee with a two-part last name joined, the app rejected the account. The team tested the mapping preview with unusual names and adjusted the expression to handle spaces. They also kept a set of permanent test users with awkward names for future changes.
Scenario 4: Self-promotion by profile edit
Redfern Media let employees edit their own “team” attribute in the profile page, and a group rule granted access to an editing tool for the “Editorial” team. A curious employee changed their team and gained access. The security team made the team attribute read-only for users, sourced it from the HR system, and reviewed every other group rule for the same weakness.
What the scenarios share
Each problem came from a quiet assumption about data: spelling, blanks, unusual names, or who could edit a field. Each fix combined a clearer expression, a cleaner data source, and a test person who would have caught the problem earlier.
Most expression incidents are data incidents. The expression only reveals what the data was always doing.
29Frequently Asked Questions
Short answers to common questions. Where details depend on your license, engine, or release, confirm them in your own console and the official documentation.
30Summary and Key Takeaways
Here is the whole story in a few lines you can carry with you.
Okta Expression Language is a small formula language that lets Okta make decisions from profile data. It reads facts, applies functions and operators, and returns either a yes or no answer or a piece of text. You meet it in group rules, attribute mappings, claims and statements, and in places where policy screens allow custom conditions. Because the same words can mean different things in different places, and because the data behind them can be messy, the habits that matter most are simple: keep expressions small, guard against blanks, normalize text, protect the data that security relies on, and test with real sample people.
Use the simplest tool that works. Built-in conditions and groups come first, clean attributes next, and custom expressions last. Whatever you build, write it first as a plain sentence, give it a name and an owner, and review it on a schedule. With these habits, expressions become a quiet, dependable part of your access design instead of a hidden source of surprises.
Key Takeaways
- Expressions are small formulas over profile data. They return a yes or no answer or a piece of text, depending on where they are used.
- Context matters. Group rules, mappings, claims, and policy conditions each offer different objects, so retest anything you copy.
- Data quality decides outcomes. Most surprises come from spelling, case, and blanks, so normalize and guard.
- Security depends on who can edit the facts. Base access on trusted, read-only attributes and limit who can edit rules.
- Prefer the simplest tool. Use built-in conditions and groups first, and custom expressions only when needed.
- Test and observe. Trace five sample people, use previews, read the System Log, and watch group sizes for drift.
- Document and verify. Name, describe, and assign an owner, and confirm exact syntax and availability in the official documentation.