Azure / Azure/data-api-builder

[Enh]: Session Context Claim Forwarding

Open
#3,313 0 comments 0 reactions 0 assignees View on GitHub
2.x auth enhancement
Dominant language
C#
Stars
1.5k
Forks
370
Avg merge
3d 22h
Merged PRs (30d)
9

Description

See also: https://github.com/Azure/data-api-builder/issues/3078

## Config (Data Source Options Section)

```json
{
"data-source": {
"database-type": "mssql",
"connection-string": "@env('SQL_CONN')",
"options": {
"set-session-context": true,
"session-context-claims": [ // new
"sub",
"preferred_username",
"realm_access.roles",
"https://example.com/tenant"
]
}
}
}
```

## What?

Add `session-context-claims` under `data-source.options` to control exactly which JWT claims are forwarded to SQL Server's `sp_set_session_context`.

## Why?

Today, when `set-session-context` is `true`, DAB forwards all claims from the authenticated identity. Users have no control over which claims are sent or how nested and namespaced claims are resolved.

A simpler model is to resolve the configured claim path and store its value in `SESSION_CONTEXT` as text. Scalar values are stored as text. Arrays and objects are stored as serialized JSON text. SQL can then use `OPENJSON` and other JSON functions when needed.

## Samples

### Simple string claim

```jsonc
// JWT Payload
{ "sub": "abc123" }
```

```jsonc
// DAB Config
"session-context-claims": [
"sub"
]
```

```sql
-- SESSION_CONTEXT
sub → "abc123"
```

### Named string claim

```jsonc
// JWT Payload
{ "preferred_username": "bob" }
```

```jsonc
// DAB Config
"session-context-claims": [
"preferred_username"
]
```

```sql
-- SESSION_CONTEXT
preferred_username → "bob"
```

### Flat array claim

```jsonc
// JWT Payload
{ "roles": ["admin","writer"] }
```

```jsonc
// DAB Config
"session-context-claims": [
"roles"
]
```

```sql
-- SESSION_CONTEXT (stored as JSON text)
roles → '["admin","writer"]'
```

### Nested array claim

```jsonc
// JWT Payload
{ "realm_access": { "roles": ["editor"] } }
```

```jsonc
// DAB Config
"session-context-claims": [
"realm_access.roles"
]
```

```sql
-- SESSION_CONTEXT (stored as JSON text)
realm_access.roles → '["editor"]'
```

### Namespaced string claim

```jsonc
// JWT Payload
{ "https://example.com/tenant": "contoso" }
```

```jsonc
// DAB Config
"session-context-claims": [
"https://example.com/tenant"
]
```

```sql
-- SESSION_CONTEXT
https://example.com/tenant → "contoso"
```

### Namespaced array claim

```jsonc
// JWT Payload
{ "https://example.com/groups": ["A","B"] }
```

```jsonc
// DAB Config
"session-context-claims": [
"https://example.com/groups"
]
```

```sql
-- SESSION_CONTEXT (stored as JSON text)
https://example.com/groups → '["A","B"]'
```

### Object claim

```jsonc
// JWT Payload
{ "profile": { "name": "Bob", "region": "west" } }
```

```jsonc
// DAB Config
"session-context-claims": [
"profile"
]
```

```sql
-- SESSION_CONTEXT (stored as JSON text)
profile → '{"name":"Bob","region":"west"}'
```

## Spec: `session-context-claims`

* Type: `string[]`
* Default: `null` which means when `set-session-context` is `true`, preserve current behavior and forward all flat claims.
* Entry format: ``

* `` follows the same resolution rules as `rolesPath`: URL prefix means literal key, dot means nested traversal, otherwise literal key.

Behavior:

* Resolve the configured claim from the JWT.
* If the value is a string, number, or boolean, store its text value in `SESSION_CONTEXT`.
* If the value is an array or object, store its serialized JSON text in `SESSION_CONTEXT`.
* If the value is `null`, skip and log at `Debug`.
* SQL can parse stored JSON text using functions such as `OPENJSON`.

Notes:

* `SESSION_CONTEXT` stores scalar SQL values only. Arrays and objects are persisted as JSON text.
* Missing claim: Skip silently and do not fail the request. Log at `Debug` level: `"session-context-claims: claim '{path}' not found in JWT, skipping."`
* `SESSION_CONTEXT` key: Use the full `` string as the key name in `sp_set_session_context`.

## Backward Compatibility

| `set-session-context` | `session-context-claims` | Behavior |
| --------------------- | ------------------------ | -------------------------- |
| `false` | any | No claims forwarded |
| `true` | `null` / omitted | Forward all flat claims |
| `true` | `[]` (empty array) | Forward nothing |
| `true` | `["sub", ...]` | Forward only listed claims |

## Validation

| Case | session-context-claims | set-session-context | database-type | Result |
| ---- | -------------------------------------------- | ------------------- | ------------- | ------------------------------------------------------------------------------------------- |
| 1 | set | false | any | Startup error ("session-context-claims requires set-session-context to be true.") |
| 2 | null, empty, or whitespace entry | true | MSSQL | Startup error |
| 3 | set | true | not MSSQL | Startup warning ("session-context-claims is only supported for MSSQL.") and skip at runtime |
| 4 | null / omitted | true | MSSQL | OK (forward all flat claims) |
| 5 | empty array `[]` | true | MSSQL | OK (forward nothing) |
| 6 | valid entries | true | MSSQL | OK (forward only listed claims) |
| 7 | claim not found in JWT | true | MSSQL | Skip and log at `Debug` |
| 8 | resolved value is string, number, or boolean | true | MSSQL | Store text value |
| 9 | resolved value is array | true | MSSQL | Store serialized JSON text |
| 10 | resolved value is object | true | MSSQL | Store serialized JSON text |
| 11 | resolved value is `null` | true | MSSQL | Skip and log at `Debug` |

## Implementation

### Session Context Flow

```text
SQL query execution
→ GetSessionParamsQuery called (existing)
→ If session-context-claims is null:
→ Forward all flat claims (existing behavior)
→ If session-context-claims is non-null:
→ For each entry "path":
→ Resolve claim value (literal key / dot-path / URL key)
→ If missing: skip, log debug
→ If string, number, or boolean: convert to text and set sp_set_session_context 'path', @value
→ If array or object: serialize as JSON text and set sp_set_session_context 'path', @jsonValue
→ If null: skip and log debug
```

Contributor guide

Open the contributing guide

Research direction

Start at GetSessionParamsQuery and trace how data-source.options and set-session-context currently produce SQL Server session parameters. Then locate the related configuration validation and runtime logging paths. Done means the documented validation cases, claim-path resolution, scalar or JSON serialization, backward compatibility, and skip behavior all work as specified.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sql
Domain
api, backend, databases
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.