Using Okta Inline Hooks to Enrich User Profiles at Registration
Learn how to call an external HR system during Okta’s pre‑registration flow to add custom attributes to new user profiles, with a concrete example, setup steps, and trade‑offs to consider.
21 Sept 2025, 09:27 UTC

Problem: Keeping Okta profiles in sync with HR data at account creation
When a new employee signs up through your application, Okta creates the user account but often lacks details such as employee ID, department, or manager that live in an HR system. Relying on nightly batch imports creates a window where the profile is incomplete, and updating the application to call HR directly adds coupling and maintenance overhead.
Thesis: Okta Inline Hooks let you run custom code during the pre‑registration step to fetch and merge HR attributes without touching the application
Inline Hooks are HTTPS callbacks that Okta invokes at specific points in the authentication or user‑lifecycle flow. A pre‑registration hook receives the user data Okta is about to persist, calls your service, and can return commands to modify the profile before the account is created.
How the hook works
- Okta sends a POST request to the URL you register, containing a JSON payload with
data(the user object),authentication(info about the invoking actor), and an optionalcommandsarray. - Your service must respond within Okta’s timeout (default 2 seconds) with a JSON body that may include a
commandsarray to set or overwrite attributes. - If the hook returns a non‑2xx status, malformed JSON, or times out, Okta aborts the registration and logs the failure.
Setting up the hook
- Enable Identity Engine – Inline Hooks require an Okta Identity Engine tenant (available on Developer Edition or paid plans with the feature enabled).
- Create an external endpoint – Expose a HTTPS service (e.g., via ngrok for testing) that validates the request using a shared secret or JWT signed with Okta’s key.
- Register the hook – In the Admin Console go to Security → API → Inline Hooks or use the Okta API:
POST /api/v1/inlineHooks
Authorization: SSWS ${OKTA_API_TOKEN}
Content-Type: application/json
{
"name": "hr-pre-registration-hook",
"type": "pre-registration",
"config": {
"uri": "https://your-domain.ngrok.io/okta/pre-reg",
"headers": [
{"name": "X-Signature", "value": "${SHARED_SECRET}"}
]
}
}
Replace ${OKTA_API_TOKEN} with a token that has InlineHooksManager permissions, and ${SHARED_SECRET} with a value you will also check in your service.
Worked example: pulling employee ID and department from HR
The following pseudocode shows a minimal Node/Express handler. It validates the signature, calls an internal HR API, and returns commands to set employeeId and department on the user profile.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const SHARED_SECRET = process.env.OKTA_HOOK_SECRET;
function verifySignature(req) {
const sig = req.headers['x-signature'];
const hmac = crypto.createHmac('sha256', SHARED_SECRET);
hmac.update(JSON.stringify(req.body));
return hmac.digest('hex') === sig;
}
app.post('/okta/pre-reg', (req, res) => {
if (!verifySignature(req)) {
return res.status(401).json({error: 'Invalid signature'});
}
const user = req.body.data.profile; // Okta‑supplied profile
const externalId = user.externalId; // e.g., email or login
// Call HR system (pseudo‑call)
hrApi.getEmployee({externalId})
.then(hrData => {
const commands = [];
if (hrData.employeeId) {
commands.push({
type: "com.okta.user.profile.update",
value: {employeeId: hrData.employeeId}
});
}
if (hrData.department) {
commands.push({
type: "com.okta.user.profile.update",
value: {department: hrData.department}
});
}
res.json({commands});
})
.catch(err => {
// Log but do not fail the registration; let Okta continue with base data
console.error('HR lookup failed', err);
res.json({}); // empty commands = no changes
});
});
app.listen(3000, () => console.log('Hook listening on :3000'));
When a user registers, Okta POSTs to /okta/pre-reg. If the HR service responds within two seconds with the commands array, Okta applies the updates before persisting the account. The System Log will show a 200 response and the applied commands under the hook execution entry.
Trade‑offs and limitations
- Latency – The hook adds to the registration time; keep external calls fast (cache HR data, use connection pooling). If the hook exceeds the 2‑second timeout, Okta marks the event as failed.
- Error handling – A non‑2xx response or malformed JSON aborts the user action. Design your service to always return valid JSON, even on upstream failures, and consider a fallback that returns an empty
commandsarray. - Availability – Inline Hooks are only supported on Identity Engine tenants and require a plan that includes the feature. Classic UI tenants cannot use them.
- Scope – The hook can modify the user profile but cannot change other Okta resources (e.g., group assignments) without additional commands or post‑creation workflows.
Actionable next steps
- Spin up an Okta Developer Edition tenant, enable Identity Engine, and note your API token.
- Deploy a test endpoint (ngrok locally or a public test service) that implements the verification and HR lookup logic shown above.
- Register the hook via the Admin Console or API, using a shared secret you will also set in your service.
- Perform a test registration (e.g., via your application’s sign‑up flow) and inspect the System Log for the hook entry; verify a
200response and thatemployeeIdanddepartmentappear on the newly created user. - Monitor latency; if you regularly approach the 2‑second limit, consider caching HR data or moving enrichment to a post‑creation workflow.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.