goauthentik / goauthentik/authentik

Property-mapping test-execution endpoint doesn't enforce the same return-type rules as real token issuance, giving false confidence

Open
#24,915 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
25.6k
Forks
2k
Avg merge
1d 1h
Merged PRs (30d)
644

Description

### Summary

`POST /api/v3/propertymappings/all/{id}/test/` evaluates a `ScopeMapping`'s expression standalone and returns whatever the expression returns, reporting `"successful": true` as long as the expression doesn't raise. The real token-issuance path (`UserInfoView.get_claims()` in `authentik/providers/oauth2/views/userinfo.py`) has an additional requirement the test endpoint doesn't enforce or even warn about: **the returned value must be a `dict`**, or it's silently discarded (logged only as a `LOGGER.warning`, invisible to the admin) and contributes nothing to the token/userinfo response.

This means an admin can write a scope mapping expression like:
```python
return ["some-value"]
```
...test it via the UI/API, see `{"result": "[\"some-value\"]", "successful": true}`, reasonably conclude it works, wire up an OIDC-consuming application around it — and have it silently do nothing in every real login, with the only trace being a `LOGGER.warning` in Authentik's own server logs that the affected downstream application never sees.

### Environment

- Authentik version: `2026.5.0`

### Steps to reproduce

1. Create a custom Scope Mapping (`authentik_property_mapping_provider_scope`) with an expression that returns a plain list, e.g.:
```python
return ["consoleAdmin"]
```
2. Attach it to an OAuth2/OIDC provider's `property_mappings`, with a `scope_name` matching a scope the client will actually request.
3. Test it via `POST /api/v3/propertymappings/all/{id}/test/` with a real user PK.
4. Separately, complete a full OIDC login through that provider and inspect the resulting ID token / userinfo response (e.g. via `GET /api/v3/oauth2/access_tokens/`).

### Expected

Either:
- The test endpoint rejects/flags non-dict returns the same way `get_claims()` does, so the admin finds out immediately, or
- `get_claims()` itself is more forgiving (e.g. wraps a non-dict return under the mapping's `scope_name` as the key, rather than discarding it) — many admins would reasonably expect `return [...]` for a "groups"/"roles"-style mapping to work this way, and the current MinIO/RustFS-style downstream integrations often want exactly a bare list.

### Actual

1. The test endpoint returns `{"result": "[\"consoleAdmin\"]", "successful": true}` — appears correct.
2. The real ID token contains no trace of the mapping's intended claim at all; the only evidence anything went wrong is a `LOGGER.warning("Scope returned a non-dict value, ignoring", scope=scope, value=value)` in Authentik's server-side logs, which a typical admin debugging a downstream application (RustFS/MinIO/etc., in our case) would have no reason to go looking at.

### Suggested fix

At minimum, have the test-execution endpoint surface a warning (not just `"successful": true`) when the expression's return value wouldn't actually be included in a real token — e.g. `"successful": true, "warning": "non-dict return values are ignored during real claim assembly"`. This closes the gap between "the test tool says it works" and "it silently does nothing in production" without changing any existing behavior for expressions that already return dicts correctly.

Contributor guide

Open the contributing guide

Research direction

Start with UserInfoView.get_claims() in authentik/providers/oauth2/views/userinfo.py, then trace the POST /api/v3/propertymappings/all/{id}/test/ endpoint that evaluates the expression. Compare how non-dict returns are handled in both paths and add a visible warning for test results that real claim assembly would ignore. Done means the endpoint flags a list return while preserving successful behavior for valid dict returns.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, authentication, authorization
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
67/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.