Webiny supports Okta as an external identity provider (IDP) to replace the default Cognito authentication. First, install the @webiny/okta package (using the same version as the webiny dependency in package.json). Then create two files: an API config class that maps Okta JWT claims to Webiny identity data (OktaIdpConfig), and a React extension component (<Okta />) that wires issuer URL, client ID, and the API config path. Register the extension in webiny.config.tsx, set two environment variables (OKTA_ISSUER, OKTA_CLIENT_ID), and deploy.
Okta integration has two parts:
OktaIdpConfig.Interface that maps JWT token claims to Webiny's identity structure. Registered via OktaIdpConfig.createImplementation() (the universal DI pattern).<Okta /> from @webiny/okta, passing the issuer URL, client ID, and path to the API config file. The <Okta /> component handles environment variable injection, API extension registration, and Admin login screen setup automatically.<Okta /> Works InternallyThe <Okta /> component (from @webiny/okta) is a defineExtension that:
OKTA_ISSUER, OKTA_CLIENT_ID
REACT_APP_IDP_TYPE=okta, REACT_APP_OKTA_ISSUER, REACT_APP_OKTA_CLIENT_ID
OktaIdpFeature API extension (OIDC token verification)OktaIdpConfig.Interface| Method | Signature | Required | Description |
|---|---|---|---|
getIdentity |
(token: JwtPayload) => OktaIdentity | Promise<OktaIdentity> |
Yes | Maps JWT claims to Webiny identity data |
verifyTokenClaims |
(token: JwtPayload) => void | Promise<void> |
No | Custom claim verification (throw to reject the token) |
OktaIdentity (Return Type of getIdentity)| Field | Type | Description |
|---|---|---|
id |
string |
Unique user ID (typically token["sub"]) |
displayName |
string |
User's display name |
roles |
string[] |
Webiny security roles to assign |
teams |
string[] |
Webiny teams (optional, filter out falsy values) |
profile |
{ firstName, lastName, email } |
User profile fields |
context |
object |
Runtime data (not stored in DB) |
<Okta /> Component Props| Prop | Type | Description |
|---|---|---|
issuer |
string |
Okta issuer URL (e.g., https://dev-xxx.okta.com) |
clientId |
string |
Okta application client ID |
apiConfig |
string |
Absolute path to the API config file |
| Variable | Used By | Description |
|---|---|---|
OKTA_ISSUER |
API + Admin | Okta issuer URL |
OKTA_CLIENT_ID |
API + Admin | Okta application client ID |
Step 0: Install the @webiny/okta dependency
@webiny/okta is an optional dependency. Add it to package.json using the same version as the webiny dependency, then install:
# Check the webiny version in package.json, then add @webiny/okta with the same version
# For example, if "webiny": "^0.0.0-unstable.xxx":
yarn add @webiny/okta@^0.0.0-unstable.xxx
Important: After adding the dependency, tell the user to run
yarnto install it. Do NOT runyarnautomatically — let the user do it.
Step 1: Create the API config
Create extensions/okta/MyOktaConfig.ts:
import { OktaIdpConfig } from "@webiny/okta";
class MyIdpConfig implements OktaIdpConfig.Interface {
getIdentity(token: OktaIdpConfig.JwtPayload) {
return {
id: String(token["sub"]),
displayName: token["name"],
roles: [token["webiny_group"]],
teams: [token["team"]].filter(Boolean),
profile: {
firstName: token["first_name"],
lastName: token["last_name"],
email: token["email"]
},
context: {
canAccessTenant: true,
defaultTenant: "root"
}
};
}
}
const MyOktaConfig = OktaIdpConfig.createImplementation({
implementation: MyIdpConfig,
dependencies: []
});
export default MyOktaConfig;
Step 2: Create the extension component
Create extensions/okta/MyOktaExtension.tsx:
import React from "react";
import { Okta } from "@webiny/okta";
export const MyOktaExtension = () => {
return (
<Okta
issuer={String(process.env.OKTA_ISSUER)}
clientId={String(process.env.OKTA_CLIENT_ID)}
apiConfig={import.meta.dirname + "/MyOktaConfig.ts"}
/>
);
};
Step 3: Register in webiny.config.tsx
import React from "react";
import { MyOktaExtension } from "./extensions/okta/MyOktaExtension.js";
export const Extensions = () => {
return (
<>
{/* Replace <Cognito /> with Okta */}
<MyOktaExtension />
{/* ... other extensions ... */}
</>
);
};
Step 4: Set environment variables
Add to your .env file (or CI/CD environment):
OKTA_ISSUER=https://dev-xxxxx.okta.com/oauth2/default
OKTA_CLIENT_ID=your-okta-client-id
Step 5: Deploy
yarn webiny deploy
If your Okta setup uses custom claims that need validation:
import { OktaIdpConfig } from "@webiny/okta";
class MyIdpConfig implements OktaIdpConfig.Interface {
getIdentity(token: OktaIdpConfig.JwtPayload) {
return {
id: String(token["sub"]),
displayName: token["name"],
roles: [token["webiny_role"]],
profile: {
firstName: token["given_name"],
lastName: token["family_name"],
email: token["email"]
},
context: {
canAccessTenant: true,
defaultTenant: "root"
}
};
}
verifyTokenClaims(token: OktaIdpConfig.JwtPayload) {
// Reject tokens without the required custom claim
if (!token["webiny_role"]) {
throw new Error("Token is missing the 'webiny_role' claim.");
}
// Reject tokens from unauthorized organizations
const allowedOrgs = ["org_abc123", "org_def456"];
if (!allowedOrgs.includes(token["org_id"] as string)) {
throw new Error("User does not belong to an authorized organization.");
}
}
}
const MyOktaConfig = OktaIdpConfig.createImplementation({
implementation: MyIdpConfig,
dependencies: []
});
export default MyOktaConfig;
If your config needs access to other Webiny services (e.g., to look up tenant-specific roles):
import { OktaIdpConfig } from "@webiny/okta";
import { TenantContext } from "webiny/api/tenancy";
class MyIdpConfig implements OktaIdpConfig.Interface {
constructor(private tenantContext: TenantContext.Interface) {}
getIdentity(token: OktaIdpConfig.JwtPayload) {
const tenant = this.tenantContext.getTenant();
return {
id: String(token["sub"]),
displayName: token["name"],
roles: [token["webiny_group"]],
profile: {
firstName: token["first_name"],
lastName: token["last_name"],
email: token["email"]
},
context: {
canAccessTenant: true,
defaultTenant: tenant?.id ?? "root"
}
};
}
}
const MyOktaConfig = OktaIdpConfig.createImplementation({
implementation: MyIdpConfig,
dependencies: [TenantContext]
});
export default MyOktaConfig;
// API config
import { OktaIdpConfig } from "@webiny/okta";
// Extension component
import { Okta } from "@webiny/okta";
| Interface | Package | Purpose |
|---|---|---|
OktaIdpConfig.Interface |
@webiny/okta |
API-side JWT-to-identity mapping |
OktaIdpConfig.JwtPayload |
@webiny/okta |
JWT token payload type |
OktaIdpConfig.IdentityData |
@webiny/okta |
Identity return type |
extensions/okta/
├── MyOktaConfig.ts # API config (JWT claim mapping)
└── MyOktaExtension.tsx # Extension component (Okta setup)
In webiny.config.tsx, replace <Cognito /> with <MyOktaExtension />.
yarn webiny deploy # Deploy all (Core + API + Admin)
Both API and Admin need to be redeployed since Okta affects both the backend (token verification, identity mapping) and the frontend (login screen).
OktaIdpConfig.createImplementation()
webiny.config.tsx and extensions are organized