Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MCP with Apache Ranger and Okta on external data

Goal

Let Okta users reach SQE’s MCP endpoint from Claude Code or Codex with a browser login, and let Apache Ranger policies per Okta group decide which AWS Glue Data Catalog tables they see. Nobody handles a token by hand: the MCP client opens the browser, the user signs in to Okta, done.

Claude Code / Codex --(browser login)--> Okta --access token--> SQE /mcp
SQE: token -> Session(user, groups) -> Ranger access policies -> Glue catalog -> S3

There is no Keycloak, no Polaris and no local object store. The data is yours: the Glue Data Catalog read live, files in S3.

Components

PieceRole
sqeRuns as sqe-server --glue-catalog glue --allow-all-buckets --mcp-only. An OAuth 2.1 resource server: it only verifies Okta tokens, it never talks to Okta at login time.
Apache Ranger Admin (+ Postgres)Access policies. A Glue table is readable when a Ranger select grant matches the caller’s user, roles or groups, and reads as not found otherwise.
OktaThe only identity provider: a custom authorization server with sqe:mcp:read / sqe:mcp:write scopes and a groups claim, and a public, PKCE-only native app. Terraform for all of it is in okta/.

The bootstrap writes two policies from two Okta groups:

Okta groupSees
datalake_readonlyevery table except SENSITIVE_TABLES (explicit deny)
datalake_sensitive_roevery table including SENSITIVE_TABLES

Configuration

Every setting is an SQE_<SECTION>__<FIELD> environment override of sqe.toml, so a Fargate task definition carries the same names:

PurposeWhere
OktaSQE_AUTH__PROVIDERS__0__ISSUER, __JWKS_URL, __AUDIENCE, SQE_MCP__AUTHORIZATION_SERVERS__0
MCPSQE_MCP__RESOURCE_URI, SQE_MCP__ALLOW_WRITES; --mcp-only in the command
Glue + S3--glue-catalog, --glue-region, --glue-database, --allow-all-buckets; SQE_STORAGE__S3_REGION; AWS credentials from the shell or the task role
RangerSQE_POLICY__RANGER__URL, SQE_POLICY__RANGER__ADMIN_PASSWORD

SQE reads Glue and S3 with the credentials of its own process; nothing in the directory holds a key. On a laptop, export SSO session credentials into the shell; on Fargate, the task role is picked up by the SDK credential chain.

cp .env.example .env     # Okta issuer + client id, Glue databases, sensitive tables
eval "$(aws configure export-credentials --format env)"
./run.sh                 # starts the stack, protocol checks, writes .mcp.json
claude                   # /mcp -> sqe -> Authenticate: the browser opens Okta
./run.sh --down

The test

run.sh brings the stack up, bootstraps the Ranger policies and runs the MCP protocol checks, then writes .mcp.json for Claude Code. The login itself is the check that matters: the client gets a 401 with a WWW-Authenticate header pointing at the protected-resource metadata, reads Okta’s metadata, runs Authorization Code + PKCE in the browser, and every later call carries the access token. SQE verifies the signature against Okta’s JWKS plus iss, aud (equal to mcp.resource_uri) and exp, takes the user from sub and the groups from the groups claim, and builds the session Ranger evaluates.

Output

In Claude Code, ask for whoami, SHOW SCHEMAS FROM glue, SHOW TABLES FROM glue.<database> and a SELECT ... LIMIT 5. A user in datalake_readonly sees every table except the sensitive ones, which read as not found; a user in datalake_sensitive_ro sees them all. After a login, SQE logs Bearer token authentication succeeded with the user and roles.