Skip to content

Feat: Add authentik idp#8619

Open
Maddosaurus wants to merge 7 commits intoSSSD:masterfrom
Maddosaurus:feat-authentik
Open

Feat: Add authentik idp#8619
Maddosaurus wants to merge 7 commits intoSSSD:masterfrom
Maddosaurus:feat-authentik

Conversation

@Maddosaurus
Copy link
Copy Markdown

@Maddosaurus Maddosaurus commented Apr 20, 2026

This PR add Authentik as an available IDP provider besides Entra and Keycloak.

From a quick look around the Authentik repo, it looks they do daily test runs against their Compose deployment (see this workflow).

For testing, starting from a fresh Authentik instance (Docker compose instructions), the following additional configuration is needed:

  • Create a default device flow: Authentik Docs
  • Create a new OAuth Client:
    • Navigate to the Admin Interface
    • Applications -> Providers -> Create -> Oauth2/OpenID Provider
      • Set a provider name (e.g. sssd-prod-prov) and auth flow (e.g. default-provider-authorization-implicit-consent (Authorize Application))
      • Open "Advanced protocol settings" and add the scope authentik default OAuth Mapping: authentik API access to "Selected Scopes"
      • Create Provider
    • Applications -> Applications -> Create
      • Create a slug (e.g. sssd-prod-app and select the previously created provider (e.g. sssd-prod-prov)
      • Create Application
    • Authentik creates a service account for this application if the device flow is used for the first time ([docs](https://docs.goauthentik.io/add-secure-apps/providers/oauth2/machine_to_machine/#automatic-service-account-creation)), so let's get this created:
      • Start sssd with the new config and let it run - you likely get a 400. SSSD can be stopped.
      • It will have created a new service account (naming schema: ak-<application>-client_credentials)
    • Open the newly created Application, navigate to "Policy/Group/User bindings", select "Bind existing...", switch to "User" and select the autogenerated user for your application (e.g. "ak-sssd-prod-prov-client_credentials"), and select "Create"
    • Create a role to allow reading of users and groups: Directory -> Roles -> Create
      • Give it a name (e.g. authentik user goup read-only)
      • Open the created group -> Permissions -> Assign Permission
        • Under authentik Core select Can view Group and Can view User
      • Assign the Role to the ak service user of your application: Users -> $serviceaccout -> Roles -> Add to existing role -> select the created role

Combined with the following sssd.conf this will result in a running setup for ssh login and sudo management:

[domain/authentik]
id_provider = idp
idp_type = authentik:https://authentik.company/api/v3/core/
idp_client_id = myclient
idp_client_secret = YOUR-CLIENT-SCERET
idp_token_endpoint = https://authentik.company/application/o/token/
idp_userinfo_endpoint = https://authentik.company/application/o/userinfo/
idp_device_auth_endpoint = https://authentik.company/application/o/device/
idp_id_scope = goauthentik.io/api
idp_auth_scope = openid profile email offline_access

Note that authentik does not have application-specific endpoints for token, userinfo and device auth, but instead global endpoints. For example, the token endpoint for a given Authentik server would be https://prod.idp.tld.com/application/o/token (only authentik.company is a placeholder in this config).

Fixes #8613

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Authentik as an Identity Provider (IdP) in SSSD, including documentation, a new lookup implementation, and JSON parsing enhancements. Several critical issues were identified in the review: a syntax error due to missing parentheses in a conditional check, incorrect parameter types and redundant calls during URL encoding of device codes, and logic errors in identifying Authentik users and groups. Specifically, using 'pk' for both user and group identifiers prevents proper object differentiation, and the user lookup logic should be updated to check for the 'pk' attribute to align with Authentik's API.

Comment thread src/oidc_child/oidc_child_id.c Outdated
Comment thread src/oidc_child/oidc_child_json.c Outdated
Comment thread src/oidc_child/oidc_child_json.c Outdated
Comment thread src/providers/idp/idp_id_eval.c
@alexey-tikhonov alexey-tikhonov added the no-backport This should go to target branch only. label Apr 20, 2026
@alexey-tikhonov
Copy link
Copy Markdown
Member

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Authentik as an Identity Provider, including documentation, a new lookup implementation, and enhanced JSON parsing. Feedback points out a potential crash in parse_result due to a missing NULL check when encoding the device_code and a typo in store_json_user where uid was used instead of uuid.

Comment thread src/oidc_child/oidc_child_json.c Outdated
Comment thread src/providers/idp/idp_id_eval.c
@alexey-tikhonov alexey-tikhonov added the coverity Trigger a coverity scan label Apr 20, 2026
Comment thread src/oidc_child/oidc_child_id.c Dismissed
Comment thread src/oidc_child/oidc_child_id.c Outdated
Copy link
Copy Markdown
Contributor

@sssd-bot sssd-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review done using Claude Code / claude-opus-4-6

Functional Issues

  1. get_user_identifier regression: NULL user_identifier_attr breaks all IdP types (src/oidc_child/oidc_child_json.c:539)

    The old code handled user_identifier_attr == NULL explicitly:

    const char *id_attr_list[] = { "sub", "id", NULL };
    if (user_identifier_attr != NULL) {
        id_attr_list[0] = user_identifier_attr;
        id_attr_list[1] = NULL;
    }

    The new code:

    const char *id_attr_list[] = { user_identifier_attr, "sub", "id", NULL };

    When user_identifier_attr is NULL (which is the default — it's an optional --user-identifier-attribute CLI argument), id_attr_list[0] is NULL, terminating the loop at id_attr_list[c] != NULL immediately. The fallback attributes "sub" and "id" are never tried. This breaks user authentication for all IdP types (Entra ID, Keycloak, and Authentik) when the user-identifier-attribute is not explicitly configured.

  2. NULL pointer dereference in authentik_lookup done block (src/oidc_child/oidc_child_id.c:617-623)

    When get_json_string_array_from_json_string returns NULL at line 614, ret is set to ENOMEM at line 619, but execution falls through to add_posix_to_json_string_array(mem_ctx, ..., 0, tmp, out) at line 621 with tmp == NULL. add_posix_to_json_string_array calls json_loads(in, ...) with a NULL in, causing a crash. A goto done-style early return or an else block is needed after the NULL check (note: this function only has one label so a second error path must use return or restructure).

  3. Device code URL-encoding in shared code path may break other IdP types (src/oidc_child/oidc_child_json.c:394-399)

    parse_result() is called for all IdP types, not just Authentik. The added url_encode_string() call on the device code will encode it for every IdP when user_code is present. If Entra ID or Keycloak device codes contain characters that are already URL-safe but get modified by encoding (e.g., +, =, or %-encoded sequences getting double-encoded), this will break their device authorization flows. This change needs to be conditional on the IdP type, or it needs evidence that Entra ID and Keycloak device codes are invariant under URL encoding.

  4. get_json_string integer-to-string conversion is a global behavior change (src/oidc_child/oidc_child_json.c:42-52)

    get_json_string is called throughout the OIDC child code (device code parsing, token handling, userinfo, etc.). Making it silently convert integers to strings changes behavior for every call site. Code paths that previously received NULL for integer-typed JSON fields and treated that as "field not present" will now silently get a stringified integer. This should be a separate function (e.g., get_json_string_or_int) or the integer handling should be done at the Authentik-specific call sites.

  5. Debug message / code mismatch in store_json_user (src/providers/idp/idp_id_eval.c:66-70)

    The code checks for "uid" as fallback:

    uuid = json_object_get(user, "uid");

    But the error message says:

    "JSON user object does not contain 'id' or 'uuid' string.\n"
    

    It should say 'id' or 'uid'. This will mislead debugging.

  6. Missing :relnote: tag

    This PR adds a new user-visible IdP type (authentik). Neither commit message contains a :relnote: tag.

Nits & Non-functional Issues

  1. Missing space after if and switch keywords (src/oidc_child/oidc_child_json.c:42, src/oidc_child/oidc_child_id.c:504)

    SSSD coding style requires a space: if (...), switch (...). The PR has if(json_is_integer(tmp)) and switch(oidc_cmd).

  2. Inconsistent indentation in parse_result (src/oidc_child/oidc_child_json.c:395-399)

    The if block uses 6-space indentation instead of 4:

        dc_enc = get_json_string(dc_ctx, root, "device_code");
          if (dc_enc != NULL && dc_ctx->user_code != NULL) {
              ...
          }
  3. CamelCase variable name (src/oidc_child/oidc_child_json.c:44)

    iVal should be i_val or similar per SSSD style ("Use lower cased names with words separated by underscores").

  4. Leading whitespace before obj_id (src/oidc_child/oidc_child_id.c:563)

    Extra leading space: obj_id = get_str_attr_from_embed_json_string( — should be 4-space indent.

  5. Double blank line (src/oidc_child/oidc_child_id.c:631-632)

    Two consecutive blank lines between authentik_lookup and oidc_get_id.

  6. Extra blank line (src/oidc_child/oidc_child_json.c:541)

    Double blank line before the for loop in get_user_identifier.

  7. Trailing space before comma in talloc_asprintf calls (src/oidc_child/oidc_child_id.c:515,533,537)

    Several instances of "...%s" ,base_url with a space before the comma instead of after.

  8. Confusing boolean condition (src/oidc_child/oidc_child_id.c:512)

    if (sep == NULL && sep != input) — when sep is NULL, sep != input is always true (assuming non-NULL input), so this is equivalent to just if (sep == NULL). This is the same as the pre-existing Keycloak code at line 342, but worth cleaning up. The Entra ID version at line 122 uses the clearer if (sep == NULL || sep == input).

  9. Hard-coded page_size=2000 (src/oidc_child/oidc_child_id.c:575,579)

    The page size for group membership and user group queries is hard-coded to 2000. For environments with many groups or members, this may silently truncate results with no indication to the user.

  10. Typo "SCERET" in manpage (src/man/sssd-idp.5.xml:305)

    YOUR-CLIENT-SCERET should be YOUR-CLIENT-SECRET. (Note: this same typo exists in the pre-existing Entra ID and Keycloak examples.)

Confirmed Issues from Existing Review Comments

  1. gemini-code-assist: if(json_is_integer(tmp)) missing space — The syntax error flagged (missing parentheses) appears to have been fixed in the latest revision. The actual post-PR code at src/oidc_child/oidc_child_json.c:42 has if(json_is_integer(tmp)) which compiles but violates style (missing space after if).

  2. gemini-code-assist: url_encode_string parameter type — The latest code at src/oidc_child/oidc_child_json.c:398 correctly uses dc_ctx->rest_ctx as the first argument. This appears to have been fixed.

  3. gemini-code-assist: uid vs uuid in store_json_user — The PR author states that Authentik's API uses uid (not uuid) for users, referencing the Authentik API docs. However, the debug message at line 70 still says 'uuid' instead of 'uid', which is misleading regardless of which field name is correct.

  4. gemini-code-assist: name_and_type_identifier using pk for both user and group identifiers — The latest code at src/oidc_child/oidc_child_id.c:487-491 now correctly uses "username" for user_identifier_attr and "name" for group_identifier_attr. This appears to have been fixed.

@alexey-tikhonov
Copy link
Copy Markdown
Member

Review done using Claude Code / claude-opus-4-6

@Maddosaurus, take this with a grain of salt as some points might be off.

If you prefer, feel free to postpone addressing this until somebody from maintainers performs a review.
But if you are willing to read thru and address what makes sense, then you're welcome.

Comment thread src/oidc_child/oidc_child.c Outdated
"User identifier not found in user info data, "
"checking id token.\n");

<<<<<<< HEAD
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you accidentally committed a conflict?

Copy link
Copy Markdown
Author

@Maddosaurus Maddosaurus Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've overlooked an artifact while rebasing, thanks for flagging that.
Going forward, I'll commit any feedback I address into their own commits and then squash these into the final commits at the end

@Maddosaurus
Copy link
Copy Markdown
Author

@alexey-tikhonov I'd appreciate a human review at this point, as it seems that recent changes on master resulted in this code not working when rebased against the latest master.
I'd be grateful for some pointers about the latest changes with regards to token handling and caching, as I suspect that to be the reason this PR stopped working.
Thanks :)

@alexey-tikhonov
Copy link
Copy Markdown
Member

Could you please rebase on the top of the current 'master' branch?

@Maddosaurus
Copy link
Copy Markdown
Author

I've rebased on master - to describe the current situation:
In the auth flow, SSSD correctly displays the device code & URL and waits for the user to click the link ("Press ENTER to continue"). When clicking the link and then pressing enter, the flow fails with the following log entries:

(2026-04-23 09:32:12): [oidc_child[2461436]] [parse_result] (0x0040): [RID#8] Failed to parse json data on line [1]: [invalid escape].
(2026-04-23 09:32:12): [oidc_child[2461436]] [main] (0x0040): [RID#8] Failed to parse device code reply.
(2026-04-23 09:32:12): [oidc_child[2461436]] [main] (0x3f7c0): [RID#8] oidc_child failed!
(2026-04-23 09:32:12): [be[atikdev]] [eval_access_token_buf] (0x0040): [RID#8] Missing input.
(2026-04-23 09:32:12): [be[atikdev]] [idp_auth_done] (0x0040): [RID#8] Failed to evaluate IdP reply.
(2026-04-23 09:32:12): [be[atikdev]] [child_sig_handler] (0x0020): [RID#8] child [2461436] failed with status [1].

Authentik uses random special characters for the device code, and loading/saving the JSON object in SSSD via the jansson lib strips some of the escaping. I suspect that the device code needs to be url sanitized when loaded from the buffer (eval_access_token_buf). Do you have a recommendation how to do that correctly in this codebase?

I suspect a3c506d to be one of the reasons for a change in behaviour.

This escaping of my change is not needed anymore because of improved URL data escaping, I will remove that.

@alexey-tikhonov
Copy link
Copy Markdown
Member

This currently doesn't build:

src/oidc_child/oidc_child.c: In function ‘main’:
src/oidc_child/oidc_child.c:724:15: error: too few arguments to function ‘parse_result’; expected 2, have 1
  724 |         ret = parse_result(dc_ctx);
      |               ^~~~~~~~~~~~
In file included from src/oidc_child/oidc_child.c:31:
./src/oidc_child/oidc_child_util.h:138:9: note: declared here
  138 | errno_t parse_result(struct devicecode_ctx *dc_ctx, char *idp_type);
      |         ^~~~~~~~~~~~

@Maddosaurus
Copy link
Copy Markdown
Author

Maddosaurus commented Apr 23, 2026

I've removed the not needed function I mentioned earlier.
In this state, the code still produces the error in the logs I showed in my comment
#8619 (comment), which I marked here in the diff.

Could you advise what the best strategy would be to ensure proper url safe encoding of the token that is stored in buf and used in eval_access_token_buf?
I suspect that storing and/or loading the token (which was recently introduced) strips any escape sequences the device token might have had initially.

An example device token that Authentik returns:

<5'X!eDqr,Y5"Kt/]+tlIkc|X^%Xe7[qxA`:hz{LRQ|n~HUlBFG<"&e<yuk$}cYlnr}VVZo%HN*ff%=o&[vp,k2O,Bexgb++g`<vbY<K{cy6._0X_;)hj8@l*2~ZUzK*

@sumit-bose
Copy link
Copy Markdown
Contributor

I've removed the not needed function I mentioned earlier. In this state, the code still produces the error in the logs I showed in my comment #8619 (comment), which I marked here in the diff.

Could you advise what the best strategy would be to ensure proper url safe encoding of the token that is stored in buf and used in eval_access_token_buf? I suspect that storing and/or loading the token (which was recently introduced) strips any escape sequences the device token might have had initially.

An example device token that Authentik returns:

<5'X!eDqr,Y5"Kt/]+tlIkc|X^%Xe7[qxA`:hz{LRQ|n~HUlBFG<"&e<yuk$}cYlnr}VVZo%HN*ff%=o&[vp,k2O,Bexgb++g`<vbY<K{cy6._0X_;)hj8@l*2~ZUzK*

I've removed the not needed function I mentioned earlier. In this state, the code still produces the error in the logs I showed in my comment #8619 (comment), which I marked here in the diff.

Could you advise what the best strategy would be to ensure proper url safe encoding of the token that is stored in buf and used in eval_access_token_buf? I suspect that storing and/or loading the token (which was recently introduced) strips any escape sequences the device token might have had initially.

An example device token that Authentik returns:

<5'X!eDqr,Y5"Kt/]+tlIkc|X^%Xe7[qxA`:hz{LRQ|n~HUlBFG<"&e<yuk$}cYlnr}VVZo%HN*ff%=o&[vp,k2O,Bexgb++g`<vbY<K{cy6._0X_;)hj8@l*2~ZUzK*

Hi,

I'm a bit confused because the error in the logs you've send is coming from the oidc_child code while the code part you've highlighted is in the backend. The backend error is just a result of the error in oidc_child, imo.

Can you recompile after running configure with the --enable-sensitive-logs option and run the test again with debug_level = 9 in the [domain/...] section of sssd.conf? This should produce detailed logs with all the http data. Since the logs now might contain sensitive data like the client secret, fell free to send the logs to me directly.

bye,
Sumit

@Maddosaurus
Copy link
Copy Markdown
Author

Maddosaurus commented Apr 27, 2026

Hi,

I'm a bit confused because the error in the logs you've send is coming from the oidc_child code while the code part you've highlighted is in the backend. The backend error is just a result of the error in oidc_child, imo.

I see, thanks for the clarification. I'm not entirely sure where to escape the device code best after the recent changes. Any advice is appreciated.

Can you recompile after running configure with the --enable-sensitive-logs option and run the test again with debug_level = 9 in the [domain/...] section of sssd.conf? This should produce detailed logs with all the http data. Since the logs now might contain sensitive data like the client secret, fell free to send the logs to me directly.

bye, Sumit

You should find the logs in your inbox momentarily.
Thank you for taking a look.
Let me know if there's anything else you need.

@Maddosaurus
Copy link
Copy Markdown
Author

@sumit-bose / @alexey-tikhonov From my point of view the PR is ready for a review. I can confirm that Authentik OIDC now works correctly.

@alexey-tikhonov
Copy link
Copy Markdown
Member

@sumit-bose / @alexey-tikhonov From my point of view the PR is ready for a review. I can confirm that Authentik OIDC now works correctly.

Thank you.

Take a note it will take some time though, as we are going thru release cycle now.


token_data = json_loadb((const char *) buf, token_buflen, 0, &json_error);
if (token_data == NULL) {
// FIXME: Authentik fails at this point,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-backport This should go to target branch only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Authentik OIDC

5 participants