Protect a DaloyJS API with Okta
Okta provides workforce and customer identity with granular policies and the API Access Management add-on for issuing custom-scoped access tokens. This guide verifies tokens from a Custom Authorization Server with the built-in jwk(), which runs on every DaloyJS runtime. The official @okta/jwt-verifier is Node-only; the end of the page shows how to plug it in if you prefer it.
- 01asyncClient appOkta Custom Auth ServerUser signs in. Okta issues a scoped access token (RS256)iss = https://{domain}/oauth2/{asId}
- 02requestClient appDaloyJS APICall API with Authorization: Bearer <access token>
- 03asyncDaloyJS APIOkta JWKSjwk() fetches signing keys (cached)GET {issuer}/v1/keys
- 04noteDaloyJS APIDaloyJS APIjwk() checks issuer, audience, expiry; requireScopes() checks scp
- 05responseDaloyJS APIClient appReturn protected data after jwk() and requireScopes() pass
1. Configure an Okta Authorization Server
- In the Okta admin console, go to Security -> API -> Authorization Servers. Use the built-in
defaultserver or create a new Custom Authorization Server (requires the API Access Management license). - Add scopes (e.g.
items:read,items:write) and an access policy that allows your client app to request them. - Note the Issuer URI (e.g.
https://dev-12345.okta.com/oauth2/default) and the client's Client ID.
2. Install
Nothing beyond @daloyjs/core.
3. Environment variables
4. Verify tokens
Okta puts granted scopes in scp as an array; jwk() reads it into ctx.state.user.scopes, so requireScopes() works unchanged.
5. Guard a route
Groups
Okta does not put groups in access tokens by default. In the authorization server, add a Claims entry named groups (value type Groups, with a filter such as Matches regex .*, included in the access token). Then:
Extra claim checks
For anything beyond issuer, audience, expiry, scopes, and groups, use the verify hook. Returning false answers 403:
If the JWKS sits behind a proxy rather than under the issuer, point jwks at that https:// URL instead. jwk() refuses redirects when fetching keys.
Using @okta/jwt-verifier instead
On Node-based runtimes you can keep Okta's own SDK and plug it into bearerAuth(). Return a user object so requireScopes() and requireRoles() keep working:
The SDK imports Node modules, so it does not run on Cloudflare Workers or Fastly. ID tokens are for your front end, not your API; if you must check one, use verifier.verifyIdToken(token, clientId, nonce).
Org server vs Custom Authorization Server
Only tokens from a Custom Authorization Server are meant to be verified by your app, those issuers look like https://{domain}/oauth2/{asId}. The Org Authorization Server (https://{domain}) issues opaque tokens that only Okta should consume. Validate those via the /introspect endpoint instead, from bearerAuth({ validate }); see opaque tokens.
See also Auth0, Clerk, and the auth integrations overview.