diff --git a/includes/login.php b/includes/login.php new file mode 100644 index 0000000..830b8fe --- /dev/null +++ b/includes/login.php @@ -0,0 +1,108 @@ + +
+ + 🔐 Login mit Authentik + +
+
– oder mit Benutzername/Passwort –
+ get_error_message() ); + } + + // Userinfo holen + $userinfo = authentik_get_userinfo( $tokens['access_token'] ); + if ( is_wp_error( $userinfo ) || empty( $userinfo ) ) { + authentik_login_error( 'Benutzerinformationen konnten nicht abgerufen werden.' ); + } + + // User finden oder erstellen + $user = authentik_find_or_create_user( $userinfo ); + if ( is_wp_error( $user ) ) { + authentik_login_error( $user->get_error_message() ); + } + + // Einloggen + wp_set_auth_cookie( $user->ID, true ); + wp_set_current_user( $user->ID ); + do_action( 'wp_login', $user->user_login, $user ); + + // Weiterleitung + $redirect = admin_url(); + if ( ! user_can( $user->ID, 'manage_options' ) ) { + $redirect = home_url(); + } + + wp_redirect( $redirect ); + exit; +} + +function authentik_login_error( $message ) { + wp_redirect( wp_login_url() . '?authentik_error=' . urlencode( $message ) ); + exit; +} + +// Fehlermeldung anzeigen +add_filter( 'login_message', 'authentik_show_login_message' ); +function authentik_show_login_message( $message ) { + if ( ! empty( $_GET['authentik_error'] ) ) { + $message .= '
' . esc_html( urldecode( $_GET['authentik_error'] ) ) . '
'; + } + return $message; +} + +// Frontend: Verknüpfungs-Button für eingeloggte User (Shortcode) +add_shortcode( 'authentik_link_account', 'authentik_link_account_shortcode' ); +function authentik_link_account_shortcode() { + if ( ! is_user_logged_in() ) return ''; + + $user = wp_get_current_user(); + $subject = get_user_meta( $user->ID, 'authentik_subject', true ); + + ob_start(); + if ( $subject ) { + echo '

✓ Dein Konto ist mit Authentik verknüpft.

'; + } else { + $url = authentik_build_login_url(); + echo 'Konto mit Authentik verknüpfen'; + } + return ob_get_clean(); +} diff --git a/includes/oauth.php b/includes/oauth.php new file mode 100644 index 0000000..851a7b0 --- /dev/null +++ b/includes/oauth.php @@ -0,0 +1,73 @@ + 'code', + 'client_id' => $s['client_id'], + 'redirect_uri' => $s['redirect_uri'], + 'scope' => 'openid email profile', + 'state' => $state, + ]; + + return authentik_get_authorize_url() . '?' . http_build_query( $params ); +} + +function authentik_exchange_code( $code ) { + $s = authentik_get_settings(); + $res = wp_remote_post( authentik_get_token_url(), [ + 'timeout' => (int) $s['timeout'], + 'body' => [ + 'grant_type' => 'authorization_code', + 'code' => $code, + 'redirect_uri' => $s['redirect_uri'], + 'client_id' => $s['client_id'], + 'client_secret' => $s['client_secret'], + ], + ] ); + + if ( is_wp_error( $res ) ) { + return new WP_Error( 'token_request_failed', $res->get_error_message() ); + } + + $body = json_decode( wp_remote_retrieve_body( $res ), true ); + if ( empty( $body['access_token'] ) ) { + return new WP_Error( 'token_missing', 'Kein Access-Token erhalten.' ); + } + + return $body; +} + +function authentik_get_userinfo( $access_token ) { + $s = authentik_get_settings(); + $res = wp_remote_get( authentik_get_userinfo_url(), [ + 'timeout' => (int) $s['timeout'], + 'headers' => [ 'Authorization' => 'Bearer ' . $access_token ], + ] ); + + if ( is_wp_error( $res ) ) { + return new WP_Error( 'userinfo_failed', $res->get_error_message() ); + } + + return json_decode( wp_remote_retrieve_body( $res ), true ); +} diff --git a/includes/settings.php b/includes/settings.php new file mode 100644 index 0000000..9cb07d1 --- /dev/null +++ b/includes/settings.php @@ -0,0 +1,204 @@ + '', + 'client_secret' => '', + 'discovery_url' => '', + 'redirect_uri' => admin_url( 'admin-ajax.php?action=authentik_callback' ), + 'default_role' => 'subscriber', + 'admin_group' => 'wordpress_admin', + 'timeout' => 30, + 'create_users' => 1, + 'link_existing' => 1, + 'sync_roles' => 1, + ]; + $saved = get_option( 'authentik_settings', [] ); + return wp_parse_args( $saved, $defaults ); +} + +function authentik_settings_page() { + $s = authentik_get_settings(); + ?> +
+

Authentik Login – Einstellungen

+ + 15 ] ); + if ( ! is_wp_error( $res ) ) { + $data = json_decode( wp_remote_retrieve_body( $res ), true ); + if ( $data ) { + $map = [ + 'authorization_endpoint' => 'authorize_url', + 'token_endpoint' => 'token_url', + 'userinfo_endpoint' => 'userinfo_url', + 'jwks_uri' => 'jwks_url', + 'issuer' => 'issuer', + 'end_session_endpoint' => 'logout_url', + ]; + foreach ( $map as $key => $opt ) { + if ( isset( $data[ $key ] ) ) { + update_option( 'authentik_oidc_' . $opt, $data[ $key ] ); + } + } + echo '

Discovery-Dokument erfolgreich importiert!

'; + } + } else { + echo '

Fehler: ' . esc_html( $res->get_error_message() ) . '

'; + } + } + ?> + +
+ +

Discovery-Dokument importieren

+ + + + + +
Discovery URL + + +

Trägt alle Endpunkt-URLs automatisch ein.

+
+
+ +
+ + +

Client-Einstellungen

+ + + + + + + + + + + + + + + + + + + + + +
Client ID
Client Secret
Discovery URL + +

Wird gespeichert aber nicht direkt verwendet – nutze den Import-Button oben.

+
Redirect URI + +

Diese URI muss exakt in Authentik eingetragen sein.

+
HTTP-Timeout (Sekunden)
+ +

Benutzer-Einstellungen

+ + + + + + + + + + + + + + + + + + + + + +
Neue Benutzer erstellen> +

Erstellt automatisch einen WordPress-Account wenn kein passender User gefunden wird.

+
Bestehende User verknüpfen> +

Verknüpft Authentik-Login mit bestehendem WordPress-Account (per E-Mail oder Benutzername).

+
Rollen synchronisieren> +

Überträgt Authentik-Gruppen als WordPress-Rollen.

+
Standard-Rolle + + + +

Rolle für neue Benutzer ohne passende Authentik-Gruppe.

+
Admin-Gruppe in Authentik + +

Authentik-Gruppenname der WordPress-Administratoren werden soll (z.B. wordpress_admin).

+
+ +

Erkannte Endpunkte

+ + 'Authorize URL', + 'authentik_oidc_token_url' => 'Token URL', + 'authentik_oidc_userinfo_url' => 'Userinfo URL', + 'authentik_oidc_jwks_url' => 'JWKS URL', + 'authentik_oidc_issuer' => 'Issuer', + 'authentik_oidc_logout_url' => 'Logout URL', + ]; + foreach ( $endpoints as $opt => $label ) { + $val = get_option( $opt, '–' ); + echo ''; + } + ?> +
' . esc_html( $label ) . '' . esc_html( $val ) . '
+ + +
+
+ ID, 'authentik_subject', $subject ); + } + } + + // 3. Verknüpfe bestehenden User per Benutzername + if ( ! $user && $s['link_existing'] && $username ) { + $user = get_user_by( 'login', $username ); + if ( $user ) { + update_user_meta( $user->ID, 'authentik_subject', $subject ); + } + } + + // 4. Neuen User erstellen + if ( ! $user ) { + if ( ! $s['create_users'] ) { + return new WP_Error( 'user_not_found', 'Kein passender WordPress-Account gefunden.' ); + } + + $user_login = sanitize_user( $username ?: explode( '@', $email )[0] ); + // Sicherstellen dass Benutzername einzigartig ist + if ( username_exists( $user_login ) ) { + $user_login = $user_login . '_' . substr( $subject, 0, 6 ); + } + + $user_id = wp_insert_user( [ + 'user_login' => $user_login, + 'user_email' => $email, + 'user_pass' => wp_generate_password( 32 ), + 'display_name' => $userinfo['name'] ?? $user_login, + 'role' => $s['default_role'], + ] ); + + if ( is_wp_error( $user_id ) ) { + return $user_id; + } + + update_user_meta( $user_id, 'authentik_subject', $subject ); + $user = get_user_by( 'ID', $user_id ); + } + + // 5. Rollen synchronisieren + if ( $s['sync_roles'] && $user ) { + authentik_sync_roles( $user, $groups, $s ); + } + + return $user; +} + +function authentik_find_user_by_subject( $subject ) { + $users = get_users( [ + 'meta_key' => 'authentik_subject', + 'meta_value' => $subject, + 'number' => 1, + ] ); + return ! empty( $users ) ? $users[0] : null; +} + +function authentik_sync_roles( $user, $groups, $s ) { + $admin_group = $s['admin_group']; + + if ( $admin_group && in_array( $admin_group, $groups, true ) ) { + $user->set_role( 'administrator' ); + } else { + // Mappe Authentik-Gruppen auf WordPress-Rollen + $role_map = [ + 'wordpress_editor' => 'editor', + 'wordpress_author' => 'author', + 'wordpress_contributor' => 'contributor', + 'wordpress_subscriber' => 'subscriber', + ]; + + $assigned = false; + foreach ( $role_map as $group => $role ) { + if ( in_array( $group, $groups, true ) ) { + $user->set_role( $role ); + $assigned = true; + break; + } + } + + if ( ! $assigned ) { + // Behalte aktuelle Rolle wenn keine Gruppe passt + } + } +} + +// Profilseite: Verknüpfungsstatus anzeigen +add_action( 'show_user_profile', 'authentik_show_link_status' ); +add_action( 'edit_user_profile', 'authentik_show_link_status' ); +function authentik_show_link_status( $user ) { + $subject = get_user_meta( $user->ID, 'authentik_subject', true ); + ?> +

Authentik-Verknüpfung

+ + + + + +
Status + + ✓ Verknüpft +

Subject:

+ Verknüpfung aufheben + + ✗ Nicht verknüpft +

Beim nächsten Authentik-Login wird dieser Account automatisch verknüpft (wenn E-Mail oder Benutzername übereinstimmt).

+ +
+